• 每日一个设计模式之【建造者模式】


    每日一个设计模式之【建造者模式

    ☁️前言🎉🎉🎉

      大家好✋,我是知识汲取者😄,今天给大家带来一篇有关建造者模式的学习笔记。众所周知能够熟练使用设计模式是一个优秀程序猿的必备技能,当我们在项目中选择一个或多个合适的设计模式,不仅能大大提高项目的稳健性可移植性可维护性,同时还能让你的代码更加精炼,具备艺术美感

      本文将详细讲解建造者模式的优点、缺点、实现方式和注意事项(干货满满哦😉),相信您花个十分钟就能快速了解并掌握建造者模式的使用,让您向架构师更进一步。话不多说,大家抓紧时间上车吧😈

    上车

    推荐阅读

    • 设计模式导学:🚪传送门
    • 每日一个设计模式系列专栏:🚪传送门
    • 设计模式专属Gitee仓库:✈启程
    • 设计模式专属Github仓库:🚀上路
    • 知识汲取者的个人主页:💓点我哦

    🌻建造者模式概述

    • 什么是建造者模式

      建造者模式(Builder Pattern)是一种创建型模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

    • 建造者模式的作用:将复杂对象的构建和表示进行分离(变与不变的分离),从而能够精细化控制对象的创建过程

    • 建造者模式的优缺点

      • 优点

        • 降低了系统的耦合度。让对象的构建和表示进行了分离,提高了代码的移植性和维护性,通过是也简化了用户的操作,用户只需要知道建造者的名字,就能通过这个建造者创建一个对应的产品,而不需要知道产品的具体建造过程
        • 提高了对象创建的灵活性。对象的创建是通过一个一个的装配方法一步一步构建的,将对象的构建过程精细化了,能够通过不同的装配方法很方便的构建不同的对象
        • 提高了程序的扩展性。每个具体的建造者都互补印象,每次想要出新产品都可以直接添加一个具体的建造者,不需要修改原有的代码(符合开闭原则)(符合开闭原则)
        • 让对象的创建过程更加清晰。将复杂对象的创建进行进行了分解,使得创建过程更加清晰,也更方便使用程序来控制创建过程

        ……

      • 缺点

        • 增加了系统的复杂度。如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本
        • 使用范围受限。建造者模式只适合哪些具有共同特点产品,如果产品之间差异性过大,使用起来是非麻烦

        ……

    • 建造者模式的适用场景

      • 需要生成的对象具有复杂的内部结构,推荐使用
      • 一个产品的部分部件固定,其他部件经常变化时,推荐使用

      ……

      Java中的应用:Java中的 StringBuilder就是使用了建造者模式,再比如DomcumetBuilder、SqlSessionFactoryBuilder等都使用了建造者模式(一般带有Builder的都是使用建造者模式来创建对象的)

      生活中的应用:去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"

    • 建造者模式的角色划分

      • 抽象建造者(Builder):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口

      • 具体建造者(Concrete Builder):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象

      • 指挥者(Director):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

        它主要有两个作用:

        • 隔离了客户与对象的生成过程,让客户只需要关注对象的生成结果
        • 负责控制产品对象的生产过程,调度产品各组件的装配
      • 具体产品(Concrete Product):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程

    🌱建造者模式的实现

    示例

    问题描述

    Sunny软件公司欲开发一个视频播放软件,为了给用户使用提供方便,该播放软件提供了四种显示模式:

    1. 网络模式拥有:主窗口、控制条、菜单、播放列表、收藏列表
    2. 完整模式拥有:主窗口、控制条、菜单、播放列表
    3. 记忆模式拥有:主窗口+控制条+收藏列表
    4. 精简模式只含有:主窗口、控制条

    image-20221110112745418

    创建具体产品
    创建抽象建造者
    创建具体建造者
    创建指挥者
    编写配置文件
    编写配置文件读取类
    测试
    • 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上的代码仓库
          
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
    • 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;
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
    • 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("收藏列表");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60

      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();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
    • Step5:编写配置文件

      soft-config.xml:

      
      <config>
          <builderName>com.hhxy.builder.child.StreamlineSoftBuilderbuilderName>
          <builderName>com.hhxy.builder.child.CompleteSoftBuilderbuilderName>
          <builderName>com.hhxy.builder.child.InternetSoftBuilderbuilderName>
          <builderName>com.hhxy.builder.child.MemorySoftBuilderbuilderName>
          <builderName>testbuilderName>
      config>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 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("未找到该工厂类,请检查配置文件或者添加一个工厂类!");
              }
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
    • 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);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

      测试结果:

      image-20220929235622952

      备注:可能你会觉得这里直接用new来创建要简单很多,那是由于这个例子中的对象过于简单,在实际工作中,对象是相当复杂的,而建造者模式就是用来解决复杂对象的创建的,简单对象还是推荐直接使用new来创建,这里只是进行一个创建者模式的示范

    🌲总结

    • 建造者模式将一个对象的创建和表示进行了分离,降低了系统的耦合度,适用于复杂对象的创建。它能够精细化控制产品的装配,让一个产品诞生出不同的品种
    • 建造者模式和工厂模式的异同
      • 工厂模式建造者模式都是创建型模式,两者都是为了优化对象的创建而诞生的
      • 工厂模式的粒度更大,它主要是用来创建不同类型的对象。它着重关注产品的生产结果,不关注产品的具体生产过程,它能够生产不同类型的产品,比如:小米空调、小米汽车、小米汽车
      • 建造者模式的粒度更小,它主要用来创建复杂对象。它着重关注产品的生成细节,它能够生产不同样式的产品,比如:小米牌手机、华为牌手机、格力牌手机,这些不同样式的产品,可能具有相同的配件,也可能具有不同的配件,是通过指挥者的指挥拼装,再由建造者一步步组装而成的。在编程中,我们通常而言都是使用工厂模式去生产建造者对象,然后通过建造者去获取具体对象,比如,通过SqlSessionBuilderFacotory获取SqlSessionBuilder对象,在通过SqlSessionBuilder获取SqlSession对象;再比如通过DocumentBuilderFacotory获取DocumentBuilder对象,在通过DocumentBuilder获取Document对象
      • 工厂模式就相当于一个公司,它可以生产不同的产品;而建造者模式就相当于一个专门生产某一个类型产品的生产车间,通过流水线精细化加工,最终一步步组装成一个产品

    自此,文章就结束了,如果觉得本文对你有一丢丢帮助的话😄,欢迎点赞👍+评论✍,您的支持将是我写出更加优秀文章的动力O(∩_∩)O

    谢谢


    上一篇:每日一个设计模式之【工厂模式】

    下一篇:每日一个设计模式之【适配器模式】

    参考文章

    在次致谢

  • 相关阅读:
    Python - Windows下使用Python脚本同步一个文件夹下的所有文件到另一个文件夹下
    Mac 配置CocoaPods
    Zabbix与乐维监控对比分析(二)——Agent管理、自动发现、权限管理
    vue纯前端页面pdf导出下载
    十、性能测试之数据库测试
    javaweb 之 JDBC 详解
    使用Eclipse完成WordCount案例(Windows本地运行)
    allegro画完封装怎么老报错保存不了呢
    状态机高阶讲解-16
    有效的网络带宽监控策略
  • 原文地址:https://blog.csdn.net/qq_66345100/article/details/127785734