设计模式之建造者模式(二)

目录

  • 概述
    • 概念
    • 角色
    • 类图
    • 适用场景
  • 详述
    • 画小人业务
      • 类的介绍
      • 代码
      • 解析
    • 建造者基本代码
      • 类介绍
      • 代码
      • 解析
  • 总结
    • 设计原则
    • 其他

概述

概念

    建造者模式是一种创建型设计模式,它可以将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

角色

在建造者模式中,有以下几个重要角色:

  • 产品(Product):表示正在构建的复杂对象。它由一系列部件组成,这些部件可能是具体类或接口的实现。

  • 抽象建造者(Abstract Builder):定义了构建产品的接口,声明了创建各个部件的抽象方法。

  • 具体建造者(ConcreteBuilder):实现了抽象建造者接口,负责具体的产品构建,并实现各个部件的具体创建方法。

  • 指挥者(Director):负责安排已有的部件的建造过程,和具体建造者进行交互,以便构建最终的产品。

  • 客户端(Client):通过指挥者创建产品对象的客户端代码。

    建造者模式的核心思想是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。通过定义不同的具体建造者,可以构建出不同的产品对象,而客户端只需关心所需的产品类型和指挥者的调用即可。这样可以增加灵活性,降低了客户端与产品对象的耦合度。

类图

在这里插入图片描述

适用场景

建造者模式适用于以下情况:

  • 当要创建的对象具有复杂的内部结构时。
  • 需要通过多个步骤来构建对象。
  • 构建过程需要根据不同的配置选择不同的表示。

详述

    理解一个设计模式,可以先从概念入手,在建造者模式概念当中,有几个说的较为模糊的词语(其实是专业词汇,只是我们不太懂罢了),再来回顾下概念“将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示”,这里的复杂对象在前面的角色当中有说过,就是product,那什么叫构建和表示分离呢,构建是什么,表示是什么。接下来结合代码和业务说下我的理解。

画小人业务

类的介绍

  • PersonBuilder 抽象建造者类:定义了构建产品各个部件的抽象方法,以及保存产品对象的引用。在例子中,它定义了画人物各个部分的抽象方法,并保存了 Graphics 类型的引用。

  • PersonThinBuilder 具体建造者类:实现了抽象建造者类中定义的抽象方法,构建小瘦人的各个部分。在例子中,它实现了画小瘦人各个部分的方法。

  • PersonFatBuilder 具体建造者类:实现了抽象建造者类中定义的抽象方法,构建小胖人的各个部分。在例子中,它实现了画小胖人各个部分的方法。

  • PersonDirector 指挥者类:负责组织建造过程,控制具体建造者的调用顺序,最终构建出完整的产品。在例子中,它将小瘦人和小胖人的构建过程组织起来,并调用具体建造者的方法构建出完整的人物。

  • Client 客户端类:通过实例化具体建造者和指挥者对象,构建出不同类型的产品。在这里,创建了一个窗口,并在窗口中画出小瘦人和小胖人。

代码

//抽象建造者
abstract class PersonBuilder {protected Graphics g;public PersonBuilder(Graphics g){this.g=g;}public abstract void buildHead();public abstract void buildBody();public abstract void buildArmLeft();public abstract void buildArmRight();public abstract void buildLegLeft();public abstract void buildLegRight();}
//具体建造者--画小瘦人
public class PersonThinBuilder extends PersonBuilder{public PersonThinBuilder(Graphics g){super(g);}@Overridepublic void buildHead() {g.drawOval(150, 120, 30, 30);}@Overridepublic void buildBody() {g.drawRect(160, 150, 10, 50);}@Overridepublic void buildArmLeft() {g.drawLine(160, 150, 140, 200);}@Overridepublic void buildArmRight() {g.drawLine(170, 150, 190, 200);}@Overridepublic void buildLegLeft() {g.drawLine(160, 200, 145, 250);}@Overridepublic void buildLegRight() {g.drawLine(170, 200, 185, 250);}
}
//具体建造者2--画小胖人
public class PersonFatBuilder extends PersonBuilder{public PersonFatBuilder(Graphics g) {super(g);}@Overridepublic void buildHead() {g.drawOval(250, 120, 30, 30);}@Overridepublic void buildBody() {g.drawOval(245, 150, 40, 50);}@Overridepublic void buildArmLeft() {g.drawLine(250, 150, 230, 200);}@Overridepublic void buildArmRight() {g.drawLine(280, 150, 300, 200);}@Overridepublic void buildLegLeft() {g.drawLine(260, 200, 245, 250);}@Overridepublic void buildLegRight() {g.drawLine(280, 200, 185, 250);}
}
//指挥者
public class PersonDirector {private PersonBuilder pb;public PersonDirector(PersonBuilder pb) {this.pb = pb;}
public void createPerson(){pb.buildHead();pb.buildBody();pb.buildArmLeft();pb.buildArmRight();pb.buildLegLeft();pb.buildLegRight();
}
}
//客户端
public class Client extends JFrame {public Client() {setSize(400, 400);setDefaultCloseOperation(EXIT_ON_CLOSE);setLocationRelativeTo(null);}public void paint(Graphics g) {PersonThinBuilder gThin = new PersonThinBuilder(g);PersonDirector pdThin = new PersonDirector(gThin);pdThin.createPerson();PersonFatBuilder gFat = new PersonFatBuilder(g);PersonDirector pdFat = new PersonDirector(gFat);pdFat.createPerson();}public static void main(String[] args) {new Client().setVisible(true);//显示窗口}
}

    在这个例子中,可以清晰地看到建造者模式的结构和流程:抽象建造者定义产品的各个部分,具体建造者实现这些部分;指挥者组织建造过程,调用具体建造者构建出完整的产品。这种方式可以使得系统更加灵活、可扩展,并且降低了组件之间的耦合度。

解析

    本例中通过定义不同的具体建造者,可以创建出具有不同特征的产品。在示例中,通过 PersonThinBuilder 和 PersonFatBuilder 可以构建出小瘦人和小胖人两种不同类型的产品。
添加新的具体建造者并不影响已有的建造者类和指挥者类。如果需要增加一种新类型的产品,只需要创建一个新的具体建造者来实现该产品的构建过程,并在指挥者类中调用新的具体建造者即可。这样,系统的扩展性得到了保证,而无需修改已有的代码。
    指挥者类负责组织建造过程,但它不知道具体建造者的细节。具体建造者类负责构建产品的各个部分,但它不知道最终产品的组装方式。这样,指挥者和具体建造者之间的关系是松耦合的,它们之间仅通过抽象建造者进行交互。这种解耦使得指挥者类和具体建造者类可以独立变化,互不影响。
    总之,这里通过将复杂对象的构建过程分解为多个简单的部分,使得系统更加灵活、可扩展,并且减少了组件之间的依赖关系,提高了系统的可维护性和可测试性。

建造者基本代码

    根据业务了解完建造者模式之后,来看下建造者的基本代码,同时也来对比一下基本代码和上面的画小人的业务有什么不一样的地方。

类介绍

    建造者模式的基本代码包含了四个角色:产品类、抽象建造者、具体建造者和指挥者。Product 类表示产品,Builder 类是抽象建造者,ConcreteBuilder1 和 ConcreteBuilder2 是具体建造者,而 Director 是指挥者。这些角色各自承担不同的责任,协作完成产品的构建过程。而在上面的画小人的代码中,并没有product,那这两段代码有什么本质上的不同吗,先看完下面的代码。

代码

//产品
public class Product {ArrayList<String> parts = new ArrayList<String>();//添加新的产品部件public void add(String part){parts.add(part);}//列举所有的产品public void show(){for(String part:parts){System.out.println(part);}}
}
//抽象建造者
abstract class Builder {public abstract void buildPartA();public abstract void buildPartB();public abstract Product getResult();}//具体建造者1
public class ConcreteBuilder1 extends Builder {private Product product = new Product();@Overridepublic void buildPartA() {product.add("部件A");}@Overridepublic void buildPartB() {product.add("部件B");}@Overridepublic Product getResult() {return product;}
}
//具体建造者2
public class ConcreteBuilder2 extends Builder {private Product product = new Product();@Overridepublic void buildPartA() {product.add("部件X" );}@Overridepublic void buildPartB() {product.add("部件Y");}@Overridepublic Product getResult() {return product;}
}
//指挥者
public class Director {public void construct(Builder builder){builder.buildPartA();builder.buildPartB();}
}
//客户端
public class Client {public static void main(String[] args) {Director director = new Director();Builder b1 = new ConcreteBuilder1();Builder b2 = new ConcreteBuilder2();//指挥者用concreteBuilder1的方法来建造产品director.construct(b1);Product p1=b1.getResult();p1.show();//指挥者用concreteBuilder2的方法来建造产品director.construct(b2);Product p2=b2.getResult();p2.show();}
}

解析

两段代码的不同:

    1、在上面画小人的例子中, PersonFatBuilder和PersonThintBuilder使用了具体的数值来定义绘图的位置和大小,这在一些情况下可能不够灵活和可维护。通常,建议使用变量或常量来代替这些硬编码的数值,以增加代码的可读性和可扩展性。

    2、画小人的例子中没有明确定义一个产品类(Product)。建造者模式通常包含一个产品类,建造者负责创建并组装产品,而指挥者负责调用具体的建造者来创建产品。但是在画小人的例子并没有一个显式的产品类。在这里可以认为PersonBuilder和其子类(如PersonFatBuilder)兼具了建造者和产品的角色。也就是说,建造者本身也承担了产品的属性和方法,用于表示要构建的对象,(但是要注意这里的产品有具体的属性值了,这与product就有了很大的区别,后面细说),但是即便没有显式的产品类,但仍然可以理解为遵循了建造者模式的核心思想:将构建逻辑与表现逻辑分离,通过不同的建造者来创建不同的对象。建造者模式不一定非要有一个显式的产品类,这取决于具体的设计需求。在一些简单的场景中,可以将产品的属性和方法直接定义在建造者中,以简化代码结构。然而,在更复杂的场景中,使用一个独立的产品类可以更好地组织和管理产品的属性和行为。

    3、在看两段代码中建造者里的接口(看指挥者里调用的builder接口也一样,这样可能更直观),看代码:

//画小人业务中builder里的接口
public void createPerson(){pb.buildHead();pb.buildBody();pb.buildArmLeft();pb.buildArmRight();pb.buildLegLeft();pb.buildLegRight();
}
//建造者基本代码中builder里的接口
public class Director {public void construct(Builder builder){builder.buildPartA();builder.buildPartB();}
}

    这两个看起来好像只是名字不同,其实完全不一样,画小人的业务画出来的只能是小人,因为有头有胳膊有腿,但是建造者基本代码中起名是buildPartA(),再去看两个具体的builder里添加的都是什么内容(一个是“部件A/B”,另一个是“部件X/Y”), 这里buildpartA和buildpartB并不是具体的内容,只是两个规范或者说两个标准,这里不光是标准,也定义了执行的顺序,具体创建什么东西是由product来决定的, Product当中添加的参数类型,也是可以更换的,也就是用户指定的需要创建的类型。 也就是符合Builder当中定义的规范数量,并且符合product当中 add方法接收的参数的类型的产品就可以,无论是部件a,b还是部件xy ,再举一个例子,可以创建有头有身子的小人也可以创建放盐又放醋的菜。在逻辑上就好像改变父类 Builder的类型的效果。

    4、现在就来说说要引入一个独立的产品类product有什么好处:
    前面说过概念中的复杂对象指的是product。这里product也是概念中提到的“表示”,在这里是个虚的概念,所谓虚的就是谁都可以,只要符合规范,规范指的的只要数据类型是String就可以,当然这个类型也是你自己定义的。例子中指定的是String。

    ConcreteBuilder做“构建”的,构建的是product的组件(builderPartA/B),这部分的组件是由部件(部件X/Y等)构成的,规定的是每个组件里部件(最小的颗粒)的数量和顺序。在这里也可以把builderPartA/B看成是部件的“表示”。而director相对于builder来说是构建的过程,构建的是各个组件(builderPartA/B)的顺序,以及有哪些组件(不是ConcreteBuilder定义的组件都必须调用,需要谁就调用谁)。

总结

设计原则

在建造者模式中,涉及到以下几个设计原则:

  • 单一职责原则(Single Responsibility Principle):每个类应该只有一个引起它变化的原因。在建造者模式中,具体建造者负责构建产品的各个部件,指挥者负责组织建造过程,产品负责表示正在构建的复杂对象。通过将不同的责任分配给不同的类,保持了类的单一职责。

    开闭原则(Open-Closed Principle):软件实体应对扩展开放,对修改关闭。在建造者模式中,可以通过新增具体建造者来扩展不同类型的产品,而无需修改已有的代码。这样在增加新的产品类型时,不会对现有的客户端代码产生影响。

  • 依赖倒置原则(Dependency Inversion Principle):高层模块不应依赖于低层模块,二者都应依赖于抽象。在建造者模式中,指挥者与具体建造者之间的交互是通过抽象建造者接口进行的,而不是直接依赖于具体建造者。这样可以降低耦合性,并且使得具体建造者可以灵活替换。

  • 迪米特法则(Law of Demeter):一个对象应该对其他对象保持最小的了解。在建造者模式中,客户端只需要与指挥者进行交互,而不直接与具体建造者交互。这样客户端只需关注所需产品和指挥者的调用,不需要了解具体建造者的细节。

    通过遵循以上设计原则,建造者模式可以使系统更加灵活、可扩展,并且降低了组件之间的耦合度。

其他

    建造者模式适用于创建复杂对象,但如果对象结构相对简单,可以考虑使用其他创建型模式,如工厂方法模式。
    当构建过程固定且简单时,可以考虑省略抽象建造者和指挥者的角色,直接在具体建造者中进行构建。

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

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

相关文章

Python函数和模块的使用

我的博客 文章首发于公众号&#xff1a;小肖学数据分析 在开发过程中&#xff0c;函数和模块帮助我们将复杂的代码逻辑分解为可管理的部分&#xff0c;提升代码的可读性、可维护性和重用性。 本文将介绍如何在Python中有效利用函数和模块&#xff0c;提供详细的示例。 函数的…

【C++干货铺】会搜索的二叉树(BSTree)

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 前言&#xff1a; 二叉搜索树 二叉搜索树概念 二叉搜索树操作 二叉搜索树的查找 二叉搜索树的插入 二叉搜索树元素的删除 ​二叉搜索树的实现 BSTree结点 …

GraphicsProfiler 使用教程

GraphicsProfiler 使用教程 1.工具简介&#xff1a;2.Navigation介绍2.1.打开安装好的Graphics Profiler。2.2.将手机连接到计算机&#xff0c;软件会在手机中安装一个GraphicsProfiler应用(该应用是无界面的&#xff09;。2.3.Show files list2.4.Record new trace2.4.1.Appli…

TSINGSEE视频智能解决方案边缘AI智能与后端智能分析的区别与应用

视频监控与AI人工智能的结合是当今社会安全领域的重要发展趋势。随着科技的不断进步&#xff0c;视频监控系统已经不再局限于简单的录像和监视功能&#xff0c;而是开始融入人工智能技术&#xff0c;实现更加智能化的监控和安全管理。传统的监控系统往往需要人工操作来进行监控…

Windows11安装python模块transformers报错Long Path处理

Windows11安装python模块transformers报错&#xff0c;报错信息如下 ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: C:\\Users\\27467\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\\Local…

xcode 修改 target 中设备朝向崩溃

修改xcode的target中的设备朝向导致崩溃。 从日志上看好像没有什么特别的信息。 之后想了想&#xff0c;感觉这个应该还是跟xcode的配置有关系&#xff0c;不过改动的地方好像也只有plist。 就又翻腾了半天plist中的各种配置项&#xff0c;再把所有的用户权限提示相关的东西之…

重要通知!中国电信警告:用户须关闭路由器“双频合一”功能

在网络的无尽时空里&#xff0c;一场电信官方的宣战正酝酿中&#xff0c;目标锁定在我们日常生活中不可或缺的WiFi身上~ 最新消息曝光&#xff0c;竟然是路由器内藏的一个名为“双频合一”的功能引发了这场轰轰烈烈的网络风暴。 我们时常觉得WiFi就像是隐身在我们生活中的超级英…

call 和 apply:改变对象行为的秘密武器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

IntelliJ IDEA 运行 若依分离版后端

一、本地运行 一、选择打开IntelliJ IDEA项目 二、选择若依项目 如&#xff1a;java123 三、等待右下角的准备工作&#xff08;有进度条的&#xff09;完成 四、修改MySQL 五、修改资源上传目录 六、修改redis 七、然后点击运行 八、成功图 九、测试访问 二、部署服务器运行 …

初级数据结构(五)——树和二叉树的概念

文中代码源文件已上传&#xff1a;数据结构源码 <-上一篇 初级数据结构&#xff08;四&#xff09;——队列 | NULL 下一篇-> 1、树结构&#xff08;Tree&#xff09; 1.1、树结构的特点 自然界中的树由根部开始向上生长&#xff0c;随机长出分支&…

对自己的博客网站进行DOS攻击

对自己的博客网站进行DOS攻击 先说明一点,别对别人的网站进行ddos/dos攻击(dos攻击一般短时间攻击不下来),这是违法的,很多都有自动报警机制,本篇博客仅用于学习,请勿用于非法用途 安装kaili Linux 进入KALI官网,下载iso镜像文件 vmware新建虚拟机,选择自定义 点击下一步 …

ROS-ROS运行管理-工作空间覆盖;节点、话题、参数名称重名

文章目录 一、工作空间覆盖二、节点名称重名2.1 rosrun设置命名空间与重映射2.2 launch文件设置命名空间与重映射2.3 编码设置命名空间与重映射 三、话题名称设置3.1 rosrun设置话题重映射3.2 launch文件设置话题重映射3.3 编码设置话题名称 四、参数名称设置4.1 rosrun设置参数…

Github与Gitlab

学习目标 能够使用GitHub创建远程仓库并使用能够安装部署GitLab服务器能够使用GitLab创建仓库并使用掌握CI/CD的概念掌握蓝绿部署, 滚动更新,灰度发布的概念 GitHub是目前最火的开源项目代码托管平台。它是基于web的Git仓库&#xff0c;提供公有仓库和私有仓库&#xff0c;但私…

使用Go实现一个百行聊天服务器

前段时间, redis作者不是整了个c语言版本的聊天服务器嘛, 地址, 代码量拢共不过百行. 于是, 心血来潮下, 我也整了个Go语言版本. 简单来说就是实现了一个聊天室的功能. 将所有注释空行都去掉, 刚好100行实现. 废话不多说, 先上代码: package mainimport ("fmt"&quo…

SoC中跨时钟域的信号同步设计(单比特同步设计)

一、 亚稳态 在数字电路中&#xff0c;触发器是一种很常用的器件。对于任意一个触发器&#xff0c;都由其参数库文件规定了能正常使用的“建立时间”&#xff08;Setup time&#xff09;和“保持时间”&#xff08;Hold time &#xff09;两个参数。“建立时间”是指在时钟…

【MySQL学习之基础篇】多表查询

文章目录 1. 多表关系1.1. 一对多1.2. 多对多1.3. 一对一 2. 多表查询概述2.1. 数据准备2.2. 概述 3. 查询的分类3.1. 内连接查询3.2. 外连接查询3.3. 自连接3.3.1. 自连接查询3.3.2. 联合查询 3.4. 子查询3.4.1. 概述3.4.2. 标量子查询3.4.3. 列子查询3.4.4. 行子查询3.4.5. 表…

python+requests+pytest 接口自动化实现

最近工作之余拿公司的项目写了一个接口测试框架&#xff0c;功能还不是很完善&#xff0c;算是抛砖引玉了&#xff0c;欢迎各位来吐槽。 主要思路&#xff1a; ①对 requests 进行二次封装&#xff0c;做到定制化效果 ②使用 excel 存放接口请求数据&#xff0c;作为数据驱动 ③…

LeetCode(60)K 个一组翻转链表【链表】【困难】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; K 个一组翻转链表 1.题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xf…

Modbus转Profinet网关使用方法

Modbus转Profinet网关&#xff08;XD-MDPN100/200&#xff09;是用于将Modbus协议和Profinet协议进行转换并进行通迅的设备。Modbus转Profinet网关&#xff08;XD-MDPN100/200&#xff09;无论是新项目还是改造项目都可轻松配置完成通迅互联。 正确的安装和配置对于确保设备的正…

低代码核心能力详解:简化应用开发的新思路

低代码平台作为一种快速地应用开发解决方法&#xff0c;为中小企业实现数字化转型提供了机会。但是&#xff0c;对于一些刚开始触碰低代码平台的企业来说&#xff0c;了解其核心能力是很重要的。本文将详细分析低代码平台的核心能力&#xff0c;并在挑选低代码平台以前为中小企…