GoF23:建造者模式探究

147 阅读8分钟

建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。

使用多个简单的对象一步一步构建成一个复杂的对象,有点像造房子一样一步步从地基做起到万丈高楼。我想这也是为什么被称呼为建造者模式的原因吧!反正我是找不出更好的理由了。这样理解反而更容易记住。

概念:

  1. 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示!
  2. 主要作用:在用户不知道 对象的建造过程和细节 的情况下就可以直接创建复杂的对象。
  3. 如何使用:用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
  4. 解决的问题:

    • 方便用户创建复杂的对象(不需要知道实现过程)
    • 代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
    • 例子:造汽车 & 买汽车。
      1.工厂(建造者模式):负责制造汽车(组装过>程和细节在工厂内)
      2.汽车购买者(用户):你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可>>以使用了
      (不需要知道汽车是怎么组装的(车轮、车门、>发动机、方向盘等等))
      

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序,一般用来创建更为复杂的对象!


角色分析
指挥者(Director)直接和客户(Client)进行需求沟通;
沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder);
将各个部件的建造请求委派到具体的建造者(ConcreteBuilder);
各个具体建造者负责进行产品部件的构建;
最终构建成具体产品(Product)。


实现方式

研究了好久发现关于建造者模式的实现例子有好多,有造人、造车、造房子、造世界的...等好多。但归类后有两种实现方式。

  1. 通过Client、Director、Builder和Product形成的建造者模式
  2. 通过静态内部类方式实现零件无序装配化构造

          方式一

          通过Client、Director、Builder和Product形成的建造者模式!

          角色分析:

          1. 抽象建造者(builder):描述具体建造者的公共接口,一般用来定义建造细节的方法,并不涉及具体的对象部件的创建。
          2. 具体建造者(ConcreteBuilder):描述具体建造者,并实现抽象建造者公共接口。
          3. 指挥者(Director):调用具体建造者来创建复杂对象(产品)的各个部分,并按照一定顺序(流程)来建造复杂对象。
          4. 产品(Product):描述一个由一系列部件组成较为复杂的对象。

          既然是建造者模式,那么我们还是继续造房吧,其实我也想不到更简单的例子。假设造房简化为如下步骤:(1)地基(2)钢筋工程(3)铺电线(4)粉刷 ; “如果”要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品),最后验收。步骤抽象如下:

          1. 创建抽象建造者定义造房步骤
          2. 创建工人具体实现造房步骤
          3. 创建承包商指挥工人施工
          4. 验收,检查是否建造完成

          代码编写

          建造者:Builder.java

          /**
           *  建造者
           */
          abstract class Builder {
              //地基
              abstract void bulidA();
              //钢筋工程
              abstract void bulidB();
              //铺电线
              abstract void bulidC();
              //粉刷
              abstract void bulidD();
              //完工-获取产品
              abstract Product getProduct();
          }
          

          产品:Product.java

          /**
           *  产品(房子)
           */
          public class Product {
              private String buildA;
              private String buildB;
              private String buildC;
              private String buildD;
          
              public String getBuildA() {
                  return buildA;
              }
          
              public void setBuildA(String buildA) {
                  this.buildA = buildA;
              }
          
              public String getBuildB() {
                  return buildB;
              }
          
              public void setBuildB(String buildB) {
                  this.buildB = buildB;
              }
          
              public String getBuildC() {
                  return buildC;
              }
          
              public void setBuildC(String buildC) {
                  this.buildC = buildC;
              }
          
              public String getBuildD() {
                  return buildD;
              }
          
              public void setBuildD(String buildD) {
                  this.buildD = buildD;
              }
          
              @Override
              public String toString() {
                  return "Product{" +
                          "buildA='" + buildA + '\'' +
                          ", buildB='" + buildB + '\'' +
                          ", buildC='" + buildC + '\'' +
                          ", buildD='" + buildD + '\'' +
                          '}';
              }
          }
          

          具体建造者:ConcreteBuilder.java

          /**
           *  具体建造者(工人)
           */
          public class Worker extends Builder{
              private Product product;
              public Worker() {
                  product = new Product();
              }
              @Override
              void bulidA() {
                  product.setBuildA("地基");
                  System.out.println("地基");
              }
              @Override
              void bulidB() {
                  product.setBuildB("钢筋工程");
                  System.out.println("钢筋工程");
              }
              @Override
              void bulidC() {
                  product.setBuildC("铺电线");
                  System.out.println("铺电线");
              }
              @Override
              void bulidD() {
                  product.setBuildD("粉刷");
                  System.out.println("粉刷");
              }
              @Override
              Product getProduct() {
                  return product;
              }
          }
          

          指挥者:Director.java

          /**
           * Director.java * 指挥者
           */
          public class Director {
              //指挥工人按顺序造房,指挥者可以随意控制建造的顺序!
              public Product create(Builder builder) {
                  builder.bulidA();
                  builder.bulidB();
                  builder.bulidC();
                  builder.bulidD();
                  return builder.getProduct();
              }
          }
          

          测试类:Test.java

          /**
           * 测试类
           */
          public class Test {
              public static void main(String[] args) {
                  //包工头
                  Director director = new Director();
                  //包工头指挥具体的工人创建房子,生成具体的产品!
                  Product create = director.create(new ConcreteBuilder());
                  System.out.println(create.toString());
              }
          }
          

          上面示例是 Builder模式的常规用法,导演类 Director 在 Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。

          方式二

          通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品

          角色分析

          1. 抽象建造者
          2. 具体建造者
          3. 产品

          比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。

          比如:比如麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。步骤抽象如下:

          1. 创建建造者定义麦当劳的产品
          2. 创建服务员实现具体产品
          3. 服务员随意搭配套餐出售给客户

          代码

          建造者:Builder.java

          /**
           *  建造者
           */
          abstract class Builder {
              //汉堡
              abstract Builder bulidA(String mes);
              //饮料
              abstract Builder bulidB(String mes);
              //薯条
              abstract Builder bulidC(String mes);
              //甜品
              abstract Builder bulidD(String mes);
              //获取套餐
              abstract Product build();
          }
          

          产品:Product.java

          /**
           * Product.java
           *  产品(麦当劳套餐)
           */
          public class Product {
              private String buildA="汉堡";
              private String buildB="饮料";
              private String buildC="薯条";
              private String buildD="甜品";
              public String getBuildA() {
                  return buildA;
              }
              public void setBuildA(String buildA) {
                  this.buildA = buildA;
              }
              public String getBuildB() {
                  return buildB;
              }
              public void setBuildB(String buildB) {
                  this.buildB = buildB;
              }
              public String getBuildC() {
                  return buildC;
              }
              public void setBuildC(String buildC) {
                  this.buildC = buildC;
              }
              public String getBuildD() {
                  return buildD;
              }
              public void setBuildD(String buildD) {
                  this.buildD = buildD;
              }
          }
          

          具体建造者:Worker.java

          public class Worker extends Builder{
              private Product product;
              public Worker() {
                  product = new Product();
              }
          
              @Override
              Product build() {
                  return product;
              }
          
              @Override
              Builder bulidA(String mes) {
                  product.setBuildA(mes);
                  return this;
              }
          
              @Override
              Builder bulidB(String mes) {
                  product.setBuildB(mes);
                  return this;
              }
          
              @Override
              Builder bulidC(String mes) {
                  product.setBuildC(mes);
                  return this;
              }
          
              @Override
              Builder bulidD(String mes) {
                  product.setBuildD(mes);
                  return this;
              }
          }
          

          测试类:Test.java

          /**
           *  测试类
           */
          public class Test {
              public static void main(String[] args) {
                   ConcreteBuilder concreteBuilder = new ConcreteBuilder();
                   //可以自由组合,如果不组合,也有默认的套餐!
                   Product build = concreteBuilder
                          .bulidA("牛肉煲")
          //              .bulidC("全家桶")
                          .bulidD("冰淇淋")
                          .build();
                  System.out.println(build.toString());
              }
          }
          

          总结

          优点

          1. 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
          2. 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
          3. 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则“。具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。

          缺点

          1. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
          2. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

          应用场景

          1. 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
          2. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
          3. 需要生成的对象内部属性本身相互依赖。
          4. 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。

          建造者与抽象工厂模式的比较

          • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
          • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象 。
          • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!