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

目录

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

概述

概念

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

角色

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

  • 产品(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.rhkb.cn/news/217925.html

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

相关文章

@德人合科技——天锐绿盾|电脑文件防止泄密|文件、文档、图纸、源代码等透明加密保护,防泄密软件系统

德人合科技——天锐绿盾提供了一种企业办公电脑文件防止泄密的解决方案&#xff0c;该方案对文件、文档、设计图纸、源代码等进行了透明加密保护。 pc访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 透明加密是一种保护文…

图文教程:从0开始安装stable-diffusion

现在AI绘画还是挺火&#xff0c;Midjourney虽然不错&#xff0c;但是对于我来说还是挺贵的。今天我就来安一下开源的AI绘画stable-diffusion,它的缺点就是对电脑的要求比较高&#xff0c;尤其是显卡。 话不多说开搞。 访问sd的github&#xff0c;https://github.com/AUTOMATIC…

2023年全球软件开发大会(QCon广州站2023)-核心PPT资料下载

一、峰会简介 本次峰会包含&#xff1a;泛娱乐时代的边缘计算与通讯、稳定性即生命线、下一代软件架构、出海的思考、现代数据架构、AGI 与 AIGC 落地、大前端技术探索、编程语言实战、DevOps vs 平台工程、新型数据库、AIGC 浪潮下的企业出海、AIGC 浪潮下的效能智能化、数据…

智物发布MT6877平台无线AR智能眼镜参考设计,推动下一代无线AR发展

随着增强现实(AR)技术的不断发展&#xff0c;有线AR眼镜在连接和使用方面存在一些限制。为了解决这些问题&#xff0c;无线AR智能眼镜的推出势在必行。 新一代无线AR智能眼镜采用了天玑900&#xff08;MT6877&#xff09;平台作为参考设计&#xff0c;搭载了2.4GHz的八核处理器…

Python接口自动化 —— Json 数据处理实战(详解)

简介   上一篇说了关于json数据处理&#xff0c;是为了断言方便&#xff0c;这篇就带各位小伙伴实战一下。首先捋一下思路&#xff0c;然后根据思路一步一步的去实现和实战&#xff0c;不要一开始就盲目的动手和无头苍蝇一样到处乱撞&#xff0c;撞得头破血流后而放弃了。不仅…

cache教程 2.单机并发缓存

0.对原教程的一些见解 个人认为原教程中两点知识的引入不够友好。 首先是只读数据结构 ByteView 的引入使用是有点迷茫的&#xff0c;可能不能很好理解为什么需要ByteView。 第二是主体结构 Group的引入也疑惑。其实要是熟悉groupcache&#xff0c;那对结构Group的使用是清晰…

IntelliJ idea卡顿解决,我遇到的比较管用的方案

Setttings> Build, Execution,Deployment>Debugger> Data Views> Java 取消 Enable "toString()" object view; Speed up debugging in IntelliJ Yesterday, I observed painfully slow debugging in IntelliJ. Every step over or step in took almost…

网络基础(五):网络层协议介绍

目录 一、网络层 1、网络层的概念 2、网络层功能 3、IP数据包格式 二、ICMP协议 1、ICMP的作用和功能 2、ping命令的使用 2.1ping命令的通用格式 2.2ping命令的常用参数 2.3TypeCode&#xff1a;查看不同功能的ICMP报文 2.4ping出现问题 3、Tracert 4、冲突域 5、…

ChatGPT 也宕机了?如何预防 DDOS 攻击的发生

最近&#xff0c;开发人工智能聊天机器人的公司 OpenAI 遭受了一次规模较大的分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;导致其旗下的 ChatGPT 服务在短短 12 小时内遭遇了 4 次断网&#xff0c;众多用户遭受了连接失败的问题。 这次攻击事件引起了广泛的关…

【LSM tree 】Log-structured merge-tree 一种分层、有序、面向磁盘的数据结构

文章目录 前言基本原理读写流程写流程读流程 写放大、读放大和空间放大优化 前言 LSM Tree 全称是Log-structured merge-tree, 是一种分层&#xff0c;有序&#xff0c;面向磁盘的数据结构。其核心原理是磁盘批量顺序写比随机写性能高很多&#xff0c;可以通过围绕这一原理进行…

【Vue】日常错误总结(持续更新)

日常遇到的小问题汇总, 内容小篇幅少的就全放这里了, 内容多的会在Vue专栏单独分享~ 目录 【Q】 el-form-item值为 null 或 undefined显示““ 【Q】dialog内组件数据刷新总是延迟慢一拍 问题背景描述 解决方案 代码简单模拟 JS 【Q】el-input 不能输入的解决办法 方法…

IDEA 2023.3 start failed 启动失败修复

发现是 RestfulToolkit 插件有冲突导致的&#xff0c;删除插件后成功启动 open ~/Library/Application\ Support/JetBrains/IntelliJIdea2023.3/plugins参考&#xff1a;https://youtrack.jetbrains.com/issue/IDEA-340080/Critical-startup-error-after-upgrading-to-Intelli…

系统的安全性设计

要设计一个安全的系统&#xff0c;除了要了解一些前面讲到的常用的保护手段和技术措施外&#xff0c;还要对系统中可能出现的安全问题或存在的安全隐患有充分的认识&#xff0c;这样才能对系统的安全作有针对性的设计和强化&#xff0c;即“知己知彼&#xff0c;百战百胜”。 下…

12月11日作业

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&#xf…

基于ssm服装定制系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 开发技术 ssm 基于ssm服装定制系统源码和论文751 1.1项目研究的背景 困扰管理层的许多问题当中,服装定制将是广大用户们不可忽视的一块。但是管理好服装定制又面临很多麻…

【SpringBoot】从入门到精通的快速开发指南

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

PDI/Kettle-9.2.0.0-R(对应jdk1.8)源码编译问题记录及源码结构简介

目录 &#x1f4da;第一章 前言&#x1f4d7;背景&#x1f4d7;目的&#x1f4d7;总体方向 &#x1f4da;第二章 代码结构初识基本结构&#x1f4d7;代码模块详情 ⁉️问题记录❓问题一&#xff1a;代码分支哪些是发布版本❗答&#xff1a;后缀-R的版本 ❓问题二&#xff1a;50…

怎么选择合适的3ds Max云渲染农场?

3ds Max 用户日常面临的一个共同挑战便是漫长的渲染周期。作为一个强大的三维建模和渲染软件&#xff0c;3ds Max 势必需处理大量的光照、材质和阴影计算任务&#xff0c;因此&#xff0c;良好的渲染方案对从业者而言尤为重口。 一、为何考虑3ds Max云渲染? 云渲染成为了解决…

自动机器学习是什么?概念及应用

自动机器学习 (Auto Machine Learning) 的应用和方法 随着众多企业在大量场景中开始采用机器学习&#xff0c;前后期处理和优化的数据量及规模指数级增长。企业很难雇用充足的人手来完成与高级机器学习模型相关的所有工作&#xff0c;因此机器学习自动化工具是未来人工智能 (A…

【状态机FSM 序列检测 饮料机_2023.12.1】

同步状态机 概念 同步状态机&#xff08;同一脉冲边沿触发&#xff09;&#xff1a;有限个离散状态及某状之间的转移 异步状态机无法综合 分类 Moore状态机 只和状态有关&#xff0c;与输入无关 Mealy状态机 和状态和输入都有关 Mealy型比Moore型少一个状态 结构 由状态寄…