【设计模式-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.mzph.cn/news/615021.shtml

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

相关文章

【Unity】Android实现跳转应用设置(权限)

为了应对Google推荐要求&#xff0c;需要在申请权限时进行提示可跳转应用设置界面&#xff0c;开启权限来保证功能正常进行。 MainActivity.java下(继承Activity)//跳转至应用设置界面public void JumpAppSetting(){Intent intent new Intent();intent.setAction(android.pro…

Day31 贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和

贪心算法 part01 理论基础 455.分发饼干 376.摆动序列 53.最大子序和 理论基础&#xff08;转载自代码随想录&#xff09; 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 这么说有点抽象&#xff0c;来举一个例子&#xff1a; 例如&#…

Unity——VContainer的依赖注入

一、IOC控制反转和DI依赖倒置 1、IOC框架核心原理是依赖倒置原则 C#设计模式的六大原则 使用这种思想方式&#xff0c;可以让我们无需关心对象的生成方式&#xff0c;只需要告诉容器我需要的对象即可&#xff0c;而告诉容器我需要对象的方式就叫做DI&#xff08;依赖注入&…

企业级大数据安全架构(三)修改集群节点hostname

作者&#xff1a;楼高 在后续安装FreeIPA的过程中&#xff0c;要求机器名必须包含完整的域名信息。如果之前在Ambari集群节点上的机器名不符合这个要求&#xff0c;可以按照以下步骤在Ambari上修改所有节点的机器名&#xff1a; 1.部署节点说明 本次测试是三台 ambari 节点&…

在ios上z-index不起作用问题的总结

最近在维护一个H5老项目时&#xff0c;遇到一个问题&#xff0c;就是在ios上z-index不起作用&#xff0c;在安卓上样式都是好的。 项目的架构组成是vue2.x vux vuex vue-router等 用的UI组件库是vux 在页面中有一个功能点&#xff0c;就是点选择公司列表的时候&#xff0c;会…

【软件测试作业_TPshop商城】农业工程学院-测试需求分析与测试计划+自动化+性能+测试用例+报告软件缺陷+测试计划+单元测试+系统测试

1测试需求分析与测试计划 1.1 被测系统简介 1.2测试需求分析 1.2.1单元测试层面的测试需求分析 1.2.2系统测试层面的测试需求分析 1.3测试计划 1.31测试范围与任务 1.3.2 测试环境 1.3.3测试进度安排 测试用例的设计2 2.1单元测试层面的测试用例设计 2.2系统测试层面的测试用例…

安装黑群晖 过程中 报错误13 卡8% 40% 52% 55%、56%、57%、58%解决方法

安装群晖时部分机器会报各种错误卡进度条&#xff0c;例如&#xff1a;DSM6报 错误13 、DSM7 报52% 55%、56%、57%、58% 如果你是压根搜索不到ip参考这个&#xff1a;群晖引导成功之后群晖助手/Synology Assistant搜索不到机器ip解决方法 - Tank电玩&米多贝克U盘的PID和VI…

【Golang map并发报错】panic: assignment to entry in nil map

go并发写map[string]interface{}数据的时候&#xff0c;报错&#xff1a;panic: assignment to entry in nil map 多个key同时操作一个map时&#xff0c;如&#xff1a; test[key1] 1 test[key2] "a" test[key3] true 就会遇到并发nil值报错&#xff0c;什么…

linux部署apache服务部署静态网站

第一步&#xff1a;配置IP地址 第二步&#xff1a;创建挂载点 配置yum仓库 mkdir -p /media/cdrom 挂载 mount /dev/cdrom /media/cdrom 安装服务 安装yum源 启用httpd服务程序并将其加入到开机启动项中 建立网站数据保存目录&#xff0c;并创建首页文件 mkdir /home/wwwroo…

Educational Codeforces Round 153 (Rated for Div. 2)(VP-11,寒假加训)

VP时间 A.模拟 如果有&#xff08;&#xff09;就是no 如果没有就2种情况 1.&#xff08;&#xff09;&#xff08;&#xff09;&#xff08;&#xff09; 2.&#xff08;&#xff08;&#xff08;&#xff09;&#xff09;&#xff09; 1.ac B.贪心 用面值大的花色硬币…

英伟达推新AI语音识别模型Parakeet 号称优于Whisper

领先的开源对话 AI 工具包 NVIDIA NeMo宣布推出 Parakeet ASR 模型系列&#xff0c;这是一系列最先进的自动语音识别&#xff08;ASR&#xff09;模型&#xff0c;能够以出色的准确性转录英语口语。Parakeet ASR 模型与 Suno.ai 合作开发&#xff0c;是语音识别领域的一大突破&…

Jenkins相关问题及答案(2024)

1、什么是Jenkins&#xff1f; Jenkins 是一个开源的自动化服务器&#xff0c;它可以被用作持续集成和持续交付&#xff08;CI/CD&#xff09;的工具。CI/CD 是一种软件开发实践&#xff0c;目的是帮助团队更快地实现软件构建、测试和发布。Jenkins 提供了自动化的框架&#x…

WCF REST消息格式(JSON/XML)选择

可以说WebHttpBinding和WebHttpBehavior是整个Web HTTP编程模型最为核心的两个类型&#xff0c;前者主要解决消息编码问题&#xff0c;而余下的工作基本上落在了终结点行为WebHttpBehavior上。WebHttpBehavior属性HelpEnabled和AutomaticFormatSelectionEnabled是“帮助页面”与…

这些代码对比工具,你都知道吗?屎山别怕

在程序开发的过程中&#xff0c;程序员会经常对源代码以及库文件进行代码对比&#xff0c;在这篇文章里我们向大家介绍六款程序员常用的代码比较工具 WinMerge WinMerge是一款运行于Windows系统下的文件比较和合并工具&#xff0c;使用它可以非常方便地比较多个文档内容&#…

Hadoop分布式文件系统(三)

目录 一、Hadoop 1、MapReduce 1.1、理解MapReduce思想 1.2、分布式计算概念 1.3、MapReduce介绍 1.4、MapReduce特点 1.5、MapReduce局限性 1.6、MapReduce实例进程 1.7、MapReduce阶段组成 1.8、MapReduce数据类型 1.9、MapReduce官方示例 2、YARN 一、Hadoop 1…

上海亚商投顾:创业板指放量涨近2% 全市场超4400只个股上涨

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日震荡反弹&#xff0c;创业板指午后涨超2%。华为概念股爆发&#xff0c;鸿蒙方向领涨&#xff0c;创识…

原创 | 一文读懂ChatGPT中的强化学习

原文&#xff1a;原创 | 一文读懂ChatGPT中的强化学习 ChatGPT基于OpenAI的GPT-3.5创造&#xff0c;是InstructGPT的衍生产品&#xff0c;它引入了一种新的方法&#xff0c;将人类反馈纳入训练过程中&#xff0c;使模型的输出与用户的意图更好地结合。在OpenAI的2022年论文《通…

多汗症如何诊断?

多汗症的诊断需要综合考虑多个因素&#xff0c;包括患者的症状、病史、体格检查以及相关实验室检查等。 首先&#xff0c;医生会详细询问患者的症状&#xff0c;了解出汗的部位、时间、严重程度等&#xff0c;以便做出初步的诊断。同时&#xff0c;医生还会询问患者的家族史和…

【Docker】Docker安装入门教程及基本使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Docker实战》。&#x1f3af;&#x1f3af; &…

【K8S 云原生】Kurbernets集群的调度策略

目录 一、Kubernetes的list-watch机制 1、List-watch 2、创建pod的过程&#xff1a; 二、scheduler调度的过程和策略&#xff1a; 1、简介 2、预算策略&#xff1a;predicate 3、优先策略&#xff1a; 3.1、leastrequestedpriority&#xff1a; 3.2、balanceresourceal…