每日一个设计模式之【建造者模式】
☁️前言🎉🎉🎉
大家好✋,我是知识汲取者😄,今天给大家带来一篇有关建造者模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性、可移植性、可维护性,同时还能让你的代码更加精炼,具备艺术美感。
本文将详细讲解建造者模式的优点、缺点、实现方式和注意事项(干货满满哦😉),相信您花个十分钟就能快速了解并掌握建造者模式的使用,让您向架构师更进一步。话不多说,大家抓紧时间上车吧😈
推荐阅读:
🌻建造者模式概述
-
什么是建造者模式?
建造者模式(Builder Pattern)是一种创建型模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
-
建造者模式的作用:将复杂对象的构建和表示进行分离(变与不变的分离),从而能够精细化控制对象的创建过程
-
建造者模式的优缺点
-
优点:
- 降低了系统的耦合度。让对象的构建和表示进行了分离,提高了代码的移植性和维护性,通过是也简化了用户的操作,用户只需要知道建造者的名字,就能通过这个建造者创建一个对应的产品,而不需要知道产品的具体建造过程
- 提高了对象创建的灵活性。对象的创建是通过一个一个的装配方法一步一步构建的,将对象的构建过程精细化了,能够通过不同的装配方法很方便的构建不同的对象
- 提高了程序的扩展性。每个具体的建造者都互补印象,每次想要出新产品都可以直接添加一个具体的建造者,不需要修改原有的代码(符合开闭原则)(符合开闭原则)
- 让对象的创建过程更加清晰。将复杂对象的创建进行进行了分解,使得创建过程更加清晰,也更方便使用程序来控制创建过程
……
-
缺点:
- 增加了系统的复杂度。如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本
- 使用范围受限。建造者模式只适合哪些具有共同特点产品,如果产品之间差异性过大,使用起来是非麻烦
……
-
-
建造者模式的适用场景:
- 需要生成的对象具有复杂的内部结构,推荐使用
- 一个产品的部分部件固定,其他部件经常变化时,推荐使用
……
Java中的应用:Java中的 StringBuilder就是使用了建造者模式,再比如DomcumetBuilder、SqlSessionFactoryBuilder等都使用了建造者模式(一般带有Builder的都是使用建造者模式来创建对象的)
生活中的应用:去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"
-
建造者模式的角色划分:
-
抽象建造者(Builder):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是
buildPartX()
,它们用于创建复杂对象的各个部件;另一类方法是getResult()
,它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口 -
具体建造者(Concrete Builder):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象
-
指挥者(Director):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其
construct()
建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。它主要有两个作用:
- 隔离了客户与对象的生成过程,让客户只需要关注对象的生成结果
- 负责控制产品对象的生产过程,调度产品各组件的装配
-
具体产品(Concrete Product):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程
-
🌱建造者模式的实现
示例:
问题描述:
Sunny软件公司欲开发一个视频播放软件,为了给用户使用提供方便,该播放软件提供了四种显示模式:
- 网络模式拥有:主窗口、控制条、菜单、播放列表、收藏列表
- 完整模式拥有:主窗口、控制条、菜单、播放列表
- 记忆模式拥有:主窗口+控制条+收藏列表
- 精简模式只含有:主窗口、控制条
-
Step1:创建具体产品
Soft
package com.hhxy.pojo; /** * @author ghp * @date 2022/9/29 * @Title * @description */ public class Soft { /** * menu 菜单 * playList 播放列表 * window 主窗口 * control 控制条 * collection 收藏列表 */ private String menu; private String playList; private String window; private String control; private String collection; //由于篇幅有限,get、set、toString和构造方法都进行了省略,详细代码请参考Gitee或Github上的代码仓库 }
-
Step2:创建抽象建造者
package com.hhxy.builder; import com.hhxy.pojo.Soft; /** * @author ghp * @date 2022/9/29 * @Title 视频播放软件软件建造者 * @description */ public abstract class SoftBuilder { //要被构建的复杂对象 private Soft soft = new Soft(); /** * 让指挥者和具体建造者能够获取Soft对象 */ protected Soft getSoft(){ return soft; } /** * 建造主窗口 */ public abstract void builderWindow(); /** * 建造控制条 */ public abstract void builderControl(); /** * 建造菜单 */ public abstract void builderMenu(); /** * 建造播放列表 */ public abstract void builderPlayList(); /** * 建造收藏列表 */ public abstract void builderCollection(); /** * 钩子方法,用来控制子类的行为 */ public boolean isBuilderMenu(){ return false; } public boolean isBuilderPlayList(){ return false; } public boolean isBuilderCollection(){ return false; } }
-
Step3:创建具体建造者
1)CompleteSoftBuilder(完整模式):
package com.hhxy.builder.child; import com.hhxy.builder.SoftBuilder; /** * @author ghp * @date 2022/9/29 * @Title 完整模式建造者 * @description 主窗口+控制条+播放列表+菜单 */ public class CompleteSoftBuilder extends SoftBuilder { /** * 重写钩子方法,因为该显示模式不含有收藏列表(Collection) */ @Override public boolean isBuilderCollection() { return true; } /** * 建造主窗口 */ @Override public void builderWindow() { getSoft().setWindow("主窗口"); } /** * 建造控制条 */ @Override public void builderControl() { getSoft().setControl("控制条"); } /** * 建造菜单 */ @Override public void builderMenu() { getSoft().setMenu("菜单"); } /** * 建造播放列表 */ @Override public void builderPlayList() { getSoft().setPlayList("播放列表"); } /** * 建造收藏列表 */ @Override public void builderCollection() { getSoft().setCollection("收藏列表"); } }
2)InternetSoftBuilder、3)MemoerySoftBuilder、4)StreamlineSoftBuilder
和上面代码类似,具体代码请参考Gitee或Github上的代码仓库,略……
-
Step4:创建指挥者
package com.hhxy.builder; import com.hhxy.pojo.Soft; /** * @author ghp * @date 2022/9/29 * @Title 指挥者 * @description 用来调度软件各组件的装配 */ public class SoftDirector { /** * 未Soft产品装配各属性 * @param softBuilder 通过配置文件的读取可以获得该对象 * @return 具体产品 */ public static Soft construct(SoftBuilder softBuilder) { softBuilder.builderWindow(); softBuilder.builderControl(); if (!softBuilder.isBuilderMenu()) { softBuilder.builderMenu(); } if (!softBuilder.isBuilderCollection()) { softBuilder.builderCollection(); } if (!softBuilder.isBuilderPlayList()) { softBuilder.builderPlayList(); } return softBuilder.getSoft(); } }
-
Step5:编写配置文件
soft-config.xml:
<?xml version="1.0" encoding="UTF-8" ?> <config> <builderName>com.hhxy.builder.child.StreamlineSoftBuilder</builderName> <builderName>com.hhxy.builder.child.CompleteSoftBuilder</builderName> <builderName>com.hhxy.builder.child.InternetSoftBuilder</builderName> <builderName>com.hhxy.builder.child.MemorySoftBuilder</builderName> <builderName>test</builderName> </config>
-
Step6:编写配置文件读取类
ReadSoftConfig:
package com.hhxy.read; import com.hhxy.builder.SoftBuilder; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.lang.reflect.Constructor; /** * @author ghp * @date 2022/9/29 * @Title * @description */ public class ReadSoftConfig { public static SoftBuilder getSoftBuilder(){ try{ //1、将配置文件加载到内存中,获取DOM对象 //1.1 获取DOM解析器工厂对象DocumentBuilderFactory,用于创建DOM解析器 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //1.2 获取DOM解析器DocumentBuilder DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //1.3 加载配置文件 // Document document = documentBuilder.parse(new FileInputStream("Factory/src/car-config.xml")); //让代码和模块名进行解耦,比上面那种方法更优 Document document = documentBuilder.parse(ReadSoftConfig.class.getResourceAsStream("/soft-config.xml")); //2、获取配置文件中的数据 //2.1 从DOM中获取指定的结点的结点列表 NodeList nodeList = document.getElementsByTagName("builderName"); //2.2 获取指定位置的结点 Node classNode = nodeList.item(2).getFirstChild(); //2.3 获取指定结点中的数据(排除空格) String builderName = classNode.getNodeValue().trim(); //2.4 获取类名并输出 int index = builderName.lastIndexOf("."); String className = builderName.substring(index + 1); System.out.print(className+"="); //3、使用反射获取获取SoftBuilder对象 //3.1 获取类对象 Class cls = Class.forName(builderName); //3.2 获取该类对象的构造器对象 Constructor constructor = cls.getDeclaredConstructor(); //3.3 暴力反射,防止构造器私有化导致无法创建对象 constructor.setAccessible(true); //3.4 获取工厂对象 SoftBuilder softBuilder = (SoftBuilder) constructor.newInstance(); //4、返回通过配置文件获取的工厂对象 return softBuilder; } catch (Exception e) { //如果异常就打印异常信息,同时返回一个空 e.printStackTrace(); throw new RuntimeException("未找到该工厂类,请检查配置文件或者添加一个工厂类!"); } } }
-
Step7:测试
package com.hhxy.test; import com.hhxy.builder.SoftBuilder; import com.hhxy.builder.SoftDirector; import com.hhxy.pojo.Soft; import com.hhxy.read.ReadSoftConfig; /** * @author ghp * @date 2022/9/29 * @Title 测试类 * @description 用于测试建造者模式 */ public class Test { public static void main(String[] args) { /* 方式一:直接通过new获取具体建造者对象 Soft soft = SoftDirector.construct(new StreamlineSoftBuilder()); Soft soft = SoftDirector.construct(new StreamlineSoftBuilder()); Soft soft = SoftDirector.construct(new StreamlineSoftBuilder()); Soft soft = SoftDirector.construct(new StreamlineSoftBuilder()); */ //方式二:通过读取配置文件获取具体建造者对象,这种方式降低了代码的耦合度,同时也很方便测试 SoftBuilder softBuilder = ReadSoftConfig.getSoftBuilder(); Soft soft = SoftDirector.construct(softBuilder); System.out.println(soft); } }
测试结果:
备注:可能你会觉得这里直接用new来创建要简单很多,那是由于这个例子中的对象过于简单,在实际工作中,对象是相当复杂的,而建造者模式就是用来解决复杂对象的创建的,简单对象还是推荐直接使用new来创建,这里只是进行一个创建者模式的示范
🌲总结
- 建造者模式将一个对象的创建和表示进行了分离,降低了系统的耦合度,适用于复杂对象的创建。它能够精细化控制产品的装配,让一个产品诞生出不同的品种
- 建造者模式和工厂模式的异同?
- 工厂模式和建造者模式都是创建型模式,两者都是为了优化对象的创建而诞生的
- 工厂模式的粒度更大,它主要是用来创建不同类型的对象。它着重关注产品的生产结果,不关注产品的具体生产过程,它能够生产不同类型的产品,比如:小米空调、小米汽车、小米汽车
- 建造者模式的粒度更小,它主要用来创建复杂对象。它着重关注产品的生成细节,它能够生产不同样式的产品,比如:小米牌手机、华为牌手机、格力牌手机,这些不同样式的产品,可能具有相同的配件,也可能具有不同的配件,是通过指挥者的指挥拼装,再由建造者一步步组装而成的。在编程中,我们通常而言都是使用工厂模式去生产建造者对象,然后通过建造者去获取具体对象,比如,通过SqlSessionBuilderFacotory获取SqlSessionBuilder对象,在通过SqlSessionBuilder获取SqlSession对象;再比如通过DocumentBuilderFacotory获取DocumentBuilder对象,在通过DocumentBuilder获取Document对象
- 工厂模式就相当于一个公司,它可以生产不同的产品;而建造者模式就相当于一个专门生产某一个类型产品的生产车间,通过流水线精细化加工,最终一步步组装成一个产品
自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O
上一篇:每日一个设计模式之【工厂模式】
下一篇:每日一个设计模式之【适配器模式】
参考文章:
- [建造者模式 | 菜鸟教程](https://www.runoob.com/design-pattern/builder-pattern.html#:~:text=建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 一个,Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。)
- 一篇打通架构设计,Java设计模式10,建造者模式
- Java设计模式之创建型:建造者模式
- 建造者模式(Builder Pattern)总结
在次致谢