Bootstrap

设计模式之建造者模式

建造者模式(Builder Pattern)是一种创建型设计模式,旨在将一个复杂对象的构建过程与其表示分离。它允许通过一步步地构造对象,而不需要暴露对象的内部细节和构建过程。通常,这个模式适用于创建对象时需要多个步骤,而这些步骤是独立于对象类型的。

建造者模式的核心思想是:将复杂对象的创建过程分解成多个简单的步骤,并通过一个“建造者”来逐步构建这些步骤,从而得到最终的复杂对象。这个模式特别适合于需要动态创建不同类型的对象,且这些对象的构建过程可能涉及多个变化和不同的配置选项。

1. 建造者模式的结构

建造者模式通常由以下几个角色组成:

  1. Product(产品类):表示复杂对象的最终表示。建造者模式的目的是将这个复杂对象的创建过程封装起来,最终生成一个完整的产品。

  2. Builder(建造者):声明构建产品的抽象步骤,通常是一个接口或抽象类,定义了构建产品的各种部件的方法。

  3. ConcreteBuilder(具体建造者):实现了 Builder 接口,完成具体的构建过程。每个具体建造者都负责一步步地构建一个产品实例,并且在最终返回产品时,会把各个部件组合成一个完整的对象。

  4. Director(指挥者):负责指挥 Builder 的构建过程,按照特定的顺序调用 Builder 中的构建步骤。Director 类不参与产品的具体构建工作,它只关心构建的顺序和流程。

  5. Client(客户端):通过 Director 来控制建造过程,最终得到复杂的产品对象。

2. 建造者模式的实现

2.1 例子:建造一个复杂的 Computer 对象

假设我们要创建一个 Computer 对象,该对象包含多个部件:处理器(CPU)、内存(RAM)、硬盘(Storage)等。每个部件的配置可以不同,并且它们的创建过程可能较为复杂。在这种情况下,建造者模式可以帮助我们一步步构建这个对象。

2.2 代码实现
  • 产品类
public class Computer {
    private String CPU;
    private String RAM;
    private String Storage;

    public void setCPU(String CPU) {
        this.CPU = CPU;
    }

    public void setRAM(String RAM) {
        this.RAM = RAM;
    }

    public void setStorage(String Storage) {
        this.Storage = Storage;
    }

    @Override
    public String toString() {
        return "Computer [CPU=" + CPU + ", RAM=" + RAM + ", Storage=" + Storage + "]";
    }
}
  • 建造者接口
public interface Builder {
    Computer computer = new Computer();
    void buildCPU();
    void buildRAM();
    void buildStorage();

    public Computer build();
}
  • 具体建造者类
public class GamingComputerBuilder implements Builder{
     @Override
    public void buildCPU() {
        computer.setCPU("Intel i9");
    }

    @Override
    public void buildRAM() {
        computer.setRAM("32GB");
    }

    @Override
    public void buildStorage() {
        computer.setStorage("1TB SSD");
    }

    @Override
    public Computer build() {
        return computer;
    }
}
public class OfficeComputerBuilder implements Builder{
    @Override
    public void buildCPU() {
        computer.setCPU("Intel i5");
    }

    @Override
    public void buildRAM() {
        computer.setRAM("8GB");
    }

    @Override
    public void buildStorage() {
        computer.setStorage("500GB HDD");
    }

    @Override
    public Computer build() {
        return computer;
    }
}
  • 指挥者类
public class Director {
    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    public Computer construct(){
        builder.buildCPU();
        builder.buildRAM();
        builder.buildStorage();
        return builder.build();
    }
}
  • 测试程序
public class Client {
    public static void main(String[] args) {
        //通过选择不同的构造器,实现不同产品的构筑
        // Director director = new Director(new GamingComputerBuilder());
        Director director = new Director(new OfficeComputerBuilder());

        Computer computer = director.construct();
        System.out.println(computer);
    }
}
2.3 运行结果
 

在这个示例中:

  • Computer 类是复杂对象,它由多个部件(如 CPU、RAM、Storage)组成。
  • Builder 是构建 Computer 的抽象接口,定义了如何构建不同的部件。
  • GamingComputerBuilder 和 OfficeComputerBuilder 是 Builder 接口的具体实现,负责创建不同配置的 Computer 对象。
  • Director 负责指挥构建过程,它会调用 Builder 中的方法按特定的顺序构建 Computer
  • 最终,客户端通过 Director 获取到构建好的 Computer 对象。

3. 建造者模式的优缺点

优点:
  1. 分离了复杂对象的构建与表示:客户端只需要了解 DirectorBuilder,而不需要关心构建的细节。

  2. 灵活性高:可以通过不同的 Builder 实现类,灵活地创建各种配置的对象,满足不同需求。

  3. 可读性强:每一步构建操作都明确地分开了,避免了将所有构建步骤堆叠在一起的情况,从而提高了代码的可读性和可维护性。

  4. 支持增量构建:每个建造者都可以支持增量构建,客户端可以控制每个构建步骤的细节,甚至动态调整对象的构建过程。

缺点:
  1. 类的数量增加:为了实现建造者模式,通常会需要多个 Builder 类及 Director 类,这会导致类的数量增多,增加了系统的复杂度。

  2. 不适合所有情况:如果对象的构建非常简单,或者构建过程并不复杂,使用建造者模式可能显得过于复杂和冗余。

  3. 无法动态调整构建步骤:建造者模式通常在编译时就确定了构建的步骤顺序,因此它对动态调整和灵活变化的需求支持较差。

4. 应用场景

建造者模式适合以下场景:

  1. 对象构建过程复杂且有多个部分:例如,创建一个复杂的产品或对象,该对象由多个部件或配置组成。

  2. 需要构建不同表示的产品:当你需要构建不同的产品(例如,游戏电脑和办公电脑),这些产品具有相同的构建步骤,但部件的配置不同。

  3. 产品的构建过程独立于产品的具体类:如果你希望产品的构建过程与产品的具体类型分开,这时建造者模式非常适用。

5.建造者模式拓展

除了上述使用方式外,建造者模式在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果直接创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。

手机类重构示例
  • 手机类
package com.tian.pattern.builder.demo2;


public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    //私有构造方法
    private Phone(Builder builder) {
        this.cpu = builder.cpu;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainboard = builder.mainboard;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder screen(String screen) {
            this.screen = screen;
            return this;
        }
        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }
        public Builder mainboard(String mainboard) {
            this.mainboard = mainboard;
            return this;
        }

        //使用构建者创建Phone对象
        public Phone build() {
            return new Phone(this);
        }
    }
}
  • 测试类
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .setCpu("intel")
                .setScreen("三星屏幕")
                .setMemory("金士顿内存条")
                .setMainboard("华硕主板")
                .build();

        System.out.println(phone);
    }
}

;