【设计模式-6】建造者模式的实现与框架中的应用

 建造者模式又被成为生成器模式,是一种使用频率比较低,相对复杂的创建型模式,在很多源码框架中可以看到建造者的使用场景,稍后我们会在本文末尾展示几个框架的使用案例。
 建造者模式所构造的对象通常是比较复杂而且庞大的,也是按照既定的构建顺序将对象中的各个属性组装起来。与工厂模式不同的是,建造者模式主要目的是把繁琐的构建过程从产品类和工厂类中抽离出来,进一步解耦,最终实现用一套标准的制造工序制造出不同的产品。

1. 定义

建造者模式 的官方定义是:将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。建造者模式一步一步地创建一个复杂的对象,它允许用户通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的构造细节。

 官方的表达还是挺难理解的,总体意思就是,构建过程固定,构建使用的每个组件都可以通过继承或者实现 呈现出多态的特性,这依赖于抽象建造者接口和具体的建造者实现类两个组件来表示。

 在建造者模式中,通常包含4个角色:

  • 产品:复杂的产品类,建造者的最终产物,构建过程相对复杂,需要很多组件组装而成。
  • 抽象建造者:建造者接口,包含两个方便,一个是构成产品的各个属性,一个是各个属性的构建步骤方法。
  • 建造者实现:建造者接口的实现类,可以有多个,对应不同的组件实现细节,但是不会包含构建的顺序逻辑。
  • 指导者:客户端依赖的创建产品的指导者类,指导者类会通过建造者接口引入具体的建造者实现,并且包含了具体的构建顺序。

2. 代码实现

 建造者的实现逻辑想对复杂,下面展示一个酒水制造的代码案例,某个酒厂生产啤酒和红酒两类产品,对应上述的四个角色,代码实现如下:

  • 1. 产品
// 产品对象
public class Wine {// 用list封装制作工艺,表示制作的顺序是固定的private List<String> list = new ArrayList();// 1.准备原材料public void setPrepareMaterial(String prepareMaterial) {list.add(prepareMaterial);}// 2.制作工艺public void setCraftsmanShip(String craftsmanShip) {list.add(craftsmanShip);}// 3.出厂包装public void setFactoryPackage(String factoryPackage) {list.add(factoryPackage);}@Overridepublic String toString() {return "Wine{" + "list=" + list + '}';}
}
  • 2. 抽象建造者
// 抽象的建造者
public abstract class AbstractBuilder {// 自定义产品Wine wine = new Wine();// 设置原材料public abstract void setPrepareMaterial();// 设置制作工艺public abstract void setCraftsmanShip();// 设置出厂包装public abstract void setFactoryPackage();// 获取产品 - 注意这里是个钩子方法,子类可以不实现public Wine getProduct() {return wine;}
}
  • 3. 建造者实现
// 啤酒建造者-具体的子类实现
public class BeerBuilder extends AbstractBuilder {@Overridepublic void setPrepareMaterial() {wine.setPrepareMaterial("1.准备原材料:小麦 + 豆子");}@Overridepublic void setCraftsmanShip() {wine.setCraftsmanShip("2.制作工艺:发酵 + 蒸馏");}@Overridepublic void setFactoryPackage() {wine.setFactoryPackage("3.出厂包装:易拉罐 + 纸箱");}
}// 葡萄酒制造者 - 具体的子类实现
public class GrapeBuilder extends AbstractBuilder{@Overridepublic void setPrepareMaterial() {wine.setPrepareMaterial("1.准备原材料:葡萄 + 酵母");}@Overridepublic void setCraftsmanShip() {wine.setCraftsmanShip("2.制作工艺:发酵 + 地下贮存");}@Overridepublic void setFactoryPackage() {wine.setFactoryPackage("3.出厂包装:玻璃瓶 + 木箱");}
}
  • 4. 指导者
// 指挥者 - 构造产品对象
public class Director {private AbstractBuilder abstractBuilder;public Director(AbstractBuilder abstractBuilder) {this.abstractBuilder = abstractBuilder;}// 这里固定制造的工序,按照固定步骤执行public Wine createProduct() {// 第一步:设置原材料abstractBuilder.setPrepareMaterial();// 第二步:设置制作工艺abstractBuilder.setCraftsmanShip();// 第三步:设置出厂包装abstractBuilder.setFactoryPackage();// 第四步,返回产品return abstractBuilder.getProduct();}
}
  • 客户端
// 客户端
public class Client {public static void main(String[] args) {// 啤酒制造Director beerDirector = new Director(new BeerBuilder());Wine beer = beerDirector.createProduct();System.out.println(beer);// 红酒制造Director grapeDirector = new Director(new GrapeBuilder());Wine grape = grapeDirector.createProduct();System.out.println(grape);}
}

3. UML类图

 对照上述的代码Demo,我们来把这个案例的类图画出来,对应如下:
在这里插入图片描述

4. 简化

 因为建造者是属于相对复杂的一种模式,在实际的应用当中有很多种简化的写法,比如可以忽略指导者类、建造者接口等等。下面是一种实现方式,这种实现方式在很多源码中可以看到具体实现。

 通常在产品类的构造函数参数超过4个,而且这些参数中有一些是必填项,考虑使用这种建造者模式。现在我们来构建一个电脑的产品对象。

// 电脑产品对象
public class Computer {private String cpu; // 必选private String ram; // 必选private String keyboard; // 可选private String mouse; // 可选private String display; // 可选
}

 这种bean类的属性设置有两种方式,一个是构造函数入参,一个是通过set()方法入参,这两种方式都有些问题。构造函数入参,当参数较多的时候,类型相同的情况下,属性的顺序容易混乱;第二个中set()方式,一个对象会支持在很多模块中设置,因为类中的属性是可以分布设置的,所以容易出现属性状态变化造成错误。

 下面是一种简易的建造者实现方式(在实际场景中可能属性的构建过程很复杂):

  • 建造者模式
// 电脑产品对象
public class Computer {private final String cpu; // 必选private final String ram; // 必选private final String keyboard; // 可选private final String mouse; // 可选private final String display; // 可选private Computer(Computer.Builder builder) {this.cpu = builder.cpu;this.ram = builder.ram;this.keyboard = builder.keyboard;this.mouse = builder.mouse;this.display = builder.display;}public static Computer.Builder builder(String cpu, String ram) {return new Computer.Builder(cpu, ram);}public static class Builder {private String cpu; // 必选private String ram; // 必选private String keyboard; // 可选private String mouse; // 可选private String display; // 可选Builder(String cpu, String ram) {this.cpu = cpu;this.ram = ram;}public Builder cpu(String cpu) {this.cpu = cpu;return this;}public Builder ram(String ram) {this.ram = ram;return this;}public Builder keyboard(String keyboard) {this.keyboard = keyboard;return this;}public Builder mouse(String mouse) {this.mouse = mouse;return this;}public Builder display(String display) {this.display = display;return this;}public Computer build() {return new Computer(this);}}@Overridepublic String toString() {return "Computer{" + "cpu='" + cpu + '\'' + ", ram='" + ram + '\'' + ", keyboard='" + keyboard + '\''+ ", mouse='" + mouse + '\'' + ", display='" + display + '\'' + '}';}
}
  • 客户端实现
// 客户端
public class Client {public static void main(String[] args) {Computer computer = Computer.builder("cpu", "ram").cpu("cpu-1").mouse("mouse").display("display").build();System.out.println(computer);}
}

5. 总结

 建造者模式的核心在于如何一步一步地构建一个包含多个组成部件的完整对象,使用相同的构建过程可以构建不同的产品。在软件开发中,如果需要创建复杂对象,并且系统系统具备良好的灵活性和扩展性,可以考虑使用建造者模式。

  • 首先建造者模式使得客户端和产品创建的过程解耦,客户端可以不知道产品创建的内部细节;
  • 构建过程固定的情况下,构建的细节可以多变性,这就可以很方便的增加不同的构造实现,符合开闭原则;
  • 将每个属性的构建过程分解在不同的方法中,精细化管理,高内聚才能低耦合,符合单一职责。

6. 框架中的应用

  • StringBuilder和StringBuffer
  • SqlSessionBuilder
  • Lombok的@Builder注解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/339415.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用requests库测试post请求 操作流程

第一步 谷歌f12或其他抓包工具抓包&#xff0c;这里随机抓一个post请求 url&#xff1a;https://eva2.csdn.net/v3/06981375190026432f77c01bfca33e32/lts/groups/dadde766-b087-42da-8e67-d2499a520ee7/streams/a0119567-bf91-4314-ab75-f683ba6c0c0a/logs 第二步 导包 impo…

springboot邮件发送

一、 二、易出错的点 1. 关于 对于我们服务器上的静态资源&#xff0c;比如下面这张图片&#xff0c;是可以直接访问到的。 因此&#xff0c;我们要想在theamleaf中达到同样的效果&#xff0c;就要使用。 MimeMessage是JavaMail API中的一个类&#xff0c;用于表示电子邮件消…

企业的 Android 移动设备管理 (MDM) 解决方案

移动设备管理可帮助您在不影响最终用户体验的情况下&#xff0c;通过无线方式管理和保护组织的移动设备群&#xff0c;现代 MDM 解决方案还可以控制 App、内容和安全性&#xff0c;因此员工可以毫无顾虑地在托管设备上工作。移动设备管理软件可有效管理个人设备上的公司空间。M…

2024年【广东省安全员C证第四批(专职安全生产管理人员)】考试报名及广东省安全员C证第四批(专职安全生产管理人员)操作证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;】考试报名及广东省安全员C证第四批&#xff08;专职安全生产管理人员&#xff09;操作证考试&#xff0c;包含广东省安全员C证第四批&am…

SIT1050ISO具有隔离功能,1Mbps,高速 CAN 总线收发器

➢ 完全兼容“ ISO 11898 ”标准&#xff1b; ➢ 内置过温保护&#xff1b; ➢ 100kV/s 瞬态抗扰度&#xff1b; ➢ 显性超时功能&#xff1b; ➢ -40V 至 40V 的总线故障保护&#xff1b; ➢ I/O 电压范围支持 3.3V 和 5V MCU &#xff1b; ➢ 低环路延迟…

如何在 Windows10 下运行 Tensorflow 的目标检测?

看过很多博主通过 Object Detection 实现了一些皮卡丘捕捉&#xff0c;二维码检测等诸多特定项的目标检测。而我跟着他们的案例来运行的时候&#xff0c;不是 Tensorflow 版本冲突&#xff0c;就是缺少什么包&#xff0c;还有是运行官方 object_detection_tutorial 不展示图片等…

RIS 辅助无线网络:基于模型、启发式和机器学习の优化方法

目录 abstractintroduction相关研究BACKGROUND AND PROBLEM FORMULATIONS FOR OPTIMIZING RIS-AIDED WIRELESS NETWORKSA 优化RIS-AIDED无线网络的背景和问题公式RIS操作原则&#xff1a;RIS控制&#xff1a;RIS部署 B 总速率/容量最大化C 功率最小化D 能源效率最大化E 用户公平…

Springboot的配置文件详解:从入门到精通,解读配置文件的奇妙世界

目录 1、前言 2、介绍 2.1 Springboot配置文件的作用 2.2 Springboot支持的配置文件类型 2.3 Springboot配置文件的加载顺序 3、YAML配置文件 3.1 YAML基本语法介绍 3.2 YAML中的基本数据类型 3.3 YAML中的复合数据类型 3.4 YAML中的配置属性 3.5 YAML中的多环境配置…

C#,入门教程(14)——字符串与其他数据类型的转换

上一篇&#xff1a; C#&#xff0c;入门教程(13)——字符&#xff08;char&#xff09;及字符串&#xff08;string&#xff09;的基础知识https://blog.csdn.net/beijinghorn/article/details/123928151 数据只有可视化才能更好地体现其价值&#xff0c;因而 string 与 image…

Object.keys()

目录 1、Object.keys() 是什么&#xff1f; 2、Object.keys(obj) 用法&#xff1a; 2.1 如果对象是一个对象&#xff0c;会返回对象的属性名组成的数组&#xff1b; 2.2 如果对象是一个数组&#xff0c;则返回索引组成的数组&#xff1a; 2.3 如果是字符串&#xff0c;返回…

MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces(临时表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之Temporary Tablespaces&#xff08;临时表空间&#xff09;会话临时表空间会话临时表空间的磁盘分配和回收会话临时表空间的创建创建临时表和查看临时表信息会话临时表空间相关的设置参数innodb_temp_tablespaces_dir 全局临时表空间查…

力扣最热一百题——只出现一次的数字

这个合集已经很久没有更新了&#xff0c;今天来更新更新~~~ 目录 力扣题号 题目 题目描述 示例 提示 题解 Java解法一&#xff1a;Map集合 Java解法二&#xff1a;位运算 C位运算代码 力扣题号 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 下述题…