【23种设计模式】让代码来告诉你什么叫建造者模式

建造者模式

  • 建造者模式属于创建型模式(即帮使用者创建对象),它提供了一种创建对象的最佳方式
  • 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示<>
  • 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象
  • 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象 <===> 把内部的建造过程和细节隐藏起来
  • 例子:
    • 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
    • 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车时怎么组装的)
    • 可以通过工厂模式制造零件,然后使用建造者模式组装在一起,构建一个复杂对象,然后再去使用

角色分析

  • 建造者模式的四个角色
    • Product(产品角色): 一个具体的产品对象。
    • Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口/抽象类。
    • ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
    • Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个
      复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:
      负责控制产品对象的生产过程

角色

  • 在做房子的时候,可以将做房子概述为以下的步骤:打地基 => 建楼 => 装修 => 验收
  • 如果要做一个房子,首先要找到一个建筑公司(Director)进行设计,画图纸(抽象的Builder),然后找工人(具体的Builder)根据图纸进行建设,最后做好一个房子(Product
  • 那么可以根据这一个构建过程,建造出不同的房子,可以是高楼大厦,也可以是小别墅小洋房,这就是同样的构建过程可以创建不同的表示

有指挥者 - 由指挥者规定构建过程

// Product 类
/**
 * 产品 比如说时房子
 * 那么建房子就是通过set设置房子的四个属性
 * @author Hey
 */
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 + '\'' +
                '}';
    }
}
----------------------------------------------------------------------
// Builder 抽象类
/**
 * 抽象的建造者
 * 只是定义一些方法或接口
 * @author Hey
 */
public abstract class Builder {
    // 四个构建过程 
    abstract void buildA();// => 打地基
    abstract void buildB();// => 建楼
    abstract void buildC();// => 装修
    abstract void buildD();// => 装家具
    // 构建完成后的表示
    abstract Product getProduct();// 经过四个步骤之后得到产品
}
----------------------------------------------------------------------
// Builder 具体类  -- Worker类
/**
 * 工人只需要完成这几个步骤就能得到一个产品
 * 工人生产产品,但是没有人调用,这个产品就是为空
 * @author Hey
 */
public class Worker extends Builder {
    // 需要完成的产品
    private Product product;
    // 这里的意思是,产品要通过自己去new一个,而不是通过传入来获得
    // 可以理解成工人会自带材料过来施工,而不是让业主自己去买
    public Worker() {
        product = new Product();
    }

    @Override
    void buildA() {
        product.setBuildA("打地基");
        System.out.println("打地基");
    }

    @Override
    void buildB() {
        product.setBuildB("建楼");
        System.out.println("建楼");
    }

    @Override
    void buildC() {
        product.setBuildC("装修");
        System.out.println("装修");
    }

    @Override
    void buildD() {
        product.setBuildD("装家具");
        System.out.println("装家具");
    }

    @Override
    Product getProduct() {
        return product;
    }
}
----------------------------------------------------------------------
// Director 类
/**
 * 有了工人,还需要一个指挥者
 * 指挥 -- 核心,负责指挥构建一个工程,工程如何构建由他来决定
 * @author Hey
 */
public class Director {
    //指挥工人按照顺序建房子,然后负责输出结果
    public Product builder(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();
        return builder.getProduct();
    }
}
----------------------------------------------------------------------
// 测试类 调用
public class Test {
    public static void main(String[] args) {
        // 找指挥 -- 包工头
        Director director = new Director();
        // 让包工头去指挥工人完成产品
        Product product = director.builder(new Worker());
        // 获得产品
        System.out.println(product.toString());
    }
}
  • 那么当我们想切换构建顺序的时候,只需要操作指挥者就好了,这样就只需要将工人聚成一个类,这个类怎么构建生成,由指挥者来完成
  • 同样的构建过程可以创建不同的表示 ==> 就是假设有不同的工人,就可以建造不同的房子
  • 这是建造者模式的常规用法,Director在建造者模式中具有很重要的作用,它用于指导具体的构建者如何构建产品,控制调用的先后次序,并向调用者返回完整的产品类
  • 但是有些情况下需要简化系统结构,可以把Director和抽象建造者模式结合,可以通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义
  • 内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式就可以生产出不同的复杂产品
  • 比如在点餐时,点单员(具体构建者)可以随意搭配任意几种产品(零件)组成一款套餐(产品)
  • 这样就比第一种方式少了指挥者,而且因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活

没有指挥者 - 若不配置则按默认构建过程生产

/**
 * 套餐 --- 最终产品
 * @author Hey
 */
public class Product {
    //默认的套餐 - 如果不通过set设置,那么默认就是这几个
    private String A = "乌龙茶";
    private String B = "鸡蛋肠";
    private String C = "虾饺";
    private String D = "小笼包";

    public String getA() { return A; }

    public void setA(String A) { this.A= A; }

    public String getB() { return B; }

    public void setB(String B) { this.B = B; }

    public String getC() { return C; }

    public void setC(String C) { this.C = C; }

    public String getD() { return D; }

    public void setD(String D) { this.D = D; }

	@Override
    //模拟使用产品
    public String toString() {
        return "Product{" +
                "A='" + A + '\'' +
                ", B='" + B + '\'' +
                ", C='" + C + '\'' +
                ", D='" + D + '\'' +
                '}';
    }
}
----------------------------------------------------------------------
// Builder 抽象类
/**
 * 抽象建造者
 * @author Hey
 */
public abstract class Builder {

    abstract Builder buildA(String msg);  //乌龙茶
    abstract Builder buildB(String msg);  //鸡蛋肠
    abstract Builder buildC(String msg);  //虾饺
    abstract Builder buildD(String msg);  //小笼包
    // 如果用户不给建造者传任何的参数,那么出来的产品就按照默认的设置进行生产
    // 假设用户给了一些自定义的设置(这里是传入msg),那么就按照用户的需求生产

    //建造产品后输出
    abstract Product getProduct();
}
----------------------------------------------------------------------
// 具体建造者  Worker类
/**
 * 具体建造者
 * @author Hey
 */
public class Worker extends Builder {

    // 具体建造者生产产品
    private Product product;

    public Worker() {
        product = new Product();
        // 这里可以选择不给这个product赋值,那么就会使用默认值
        // 如果要赋值可以通过build方法赋值
    }

    @Override
    Builder buildA(String msg) {
        // 让具体建造者对零件根据需求进行定制
        product.setA(msg);
        // 具体建造者加工完成后送回
        return this;
    }

    @Override
    Builder buildB(String msg) {
        product.setB(msg);
        return this;
    }

    @Override
    Builder buildC(String msg) {
        product.setC(msg);
        return this;
    }

    @Override
    Builder buildD(String msg) {
        product.setD(msg);
        return this;
    }

    @Override
    Product getProduct() {
        return product;
    }
}
----------------------------------------------------------------------
// 测试类调用
/**
 * 测试类调用
 * @author Hey
 */
public class Test {

    public static void main(String[] args) {
        // 这次就没有了指挥者,那么用户就需要和工人直接进行交流
        // 先去找工人
        Worker worker = new Worker();
        // 如果不对产品进行定制,那么就会根据默认的设置进行生产
        // 获得产品
        Product product = worker.getProduct();
        // 使用产品
        System.out.println(product.toString());

        // 如果想对产品进行定制,不使用默认的,那么就可以让具体构建者根据调用者的要求来生产
        
        // 这里可以采用链式编程,因为Worker类中build方法返回的都是this当前对象
        // 比如说我不想喝乌龙茶,想喝龙井,那么就可以在build方法中传入龙井
        Product product1 = worker.buildA("龙井").buildB("肉蛋肠").getProduct();
        System.out.println(product1.toString());
        // 这样就可以在原来的基础上,自由组合,即使不组合,也可以有默认的套餐
    }
}
  • 输出结果
Product{A='乌龙茶', B='鸡蛋肠', C='虾饺', D='小笼包'}
Product{A='龙井', B='肉蛋肠', C='虾饺', D='小笼包'}

小结

  • 优点
    • 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节
    • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 具体的建造者类之间是相互独立的,这有利于系统的扩展,增加新的具体建造者无需修改原有类库的代码,符合开闭原则
  • 缺点
    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
    • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
  • 应用场景:
    (1)需要生产的产品对象有复杂的内部结构,这些产品对象具备共性
    (2)隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
    (3)适合于一个具有较多的零件(属性)的产品(对象)的创建过程
  • 建造者模式和抽象工厂模式两个都是创建型的设计方法,那这两者之间有什么不同呢?
    • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
    • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象
    • 如果将抽象工厂模式看成一个电子零件生产工厂,生产一个产品族的产品,那么建造者模式就是一个电子产品组装工厂,通过对部件的组装可以返回一个完整的电子产品

应用实例

  • 在JDK中 的应用实例 – StringBuilder
  • Appendable接口定义了多个append()方法(抽象方法), 即Appendable 为抽象建造者, 定义了抽象方法
  • AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能实例化
  • StringBuilder 既充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由AbstractStringBuilder 完成, 而StringBuilder 继承了AbstractStringBuilder
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Kobe_k/article/details/105032613