建造者模式

  • 理解方式:
    • 奶茶对象,奶茶建造者对象
    • 不断向奶茶建造者提供需要添加的小料,最后通过 build 函数检测是否能完成构建,生成符合需求的奶茶对象。
  • 解决问题:
    • 类属性多,构造方法长。
    • set 方法配置,可能需要一定的校验。
    • 不希望暴露最终对象的 set 方法,避免创建后被修改。
    • 分步骤构建对象。

1 建造者模式

1.1 解决的问题

  • 类属性多,构造方法长。
  • set 方法配置,可能需要一定的校验。
  • 不希望暴露最终对象的 set 方法,避免创建后被修改。
  • 分步骤构建对象。

下面的 director 类,相当于工厂,利用建造者模式创建对象。

1.2 模式结构

根据需求,自主选择 A-Z 中的任意步骤执行。

1.3 实现方式

public class MilkTea {
    private final String type;
    private final String size;
    private final boolean pearl;
    private final boolean ice;
 
    private MilkTea(Builder builder) {
        this.type = builder.type;
        this.size = builder.size;
        this.pearl = builder.pearl;
        this.ice = builder.ice;
    }
 
    public String getType() {        return type;    }
    public String getSize() {        return size;    }
    public boolean isPearl() {        return pearl;  }
    public boolean isIce() {        return ice;    }
	
    public static class Builder {
 
        private final String type;
        private String size = "中杯";
        private boolean pearl = true;
        private boolean ice = false;
 
        public Builder(String type) {
            this.type = type;
        }
        public Builder size(String size) {
            this.size = size;
            return this;
        }
        public Builder pearl(boolean pearl) {
            this.pearl = pearl;
            return this;
        }
        public Builder ice(boolean cold) {
            this.ice = cold;
            return this;
        }
        public MilkTea build() {
		    // 将校验逻辑放到这里,必填项、约束条件等,只有都通过了才构建对象
            return new MilkTea(this);
        }
    }
}

用户

public class User {
    private void buyMilkTea() {
        MilkTea milkTea = new MilkTea.Builder("原味").build();
        MilkTea chocolate =new MilkTea.Builder("巧克力味").ice(false).build();
        MilkTea strawberry = new MilkTea.Builder("草莓味").size("大杯").pearl(false).ice(true).build();
    }
}

另一种实现方法:

// 产品类
public class Role {
    private String name;    // 角色名
    private int sex;  // 性别:0-男、1-女
    private String face;
    private String clothe;
 
    public Role(Builder builder) {
        this.name = builder.getName();
        this.sex = builder.getSex();
        this.face = builder.getFace();
        this.clothe = builder.getClothe();
    }
}
 
// 建造者类
public class Builder {
    private static final int DEFAULT_SEX = 0;
    private static final String DEFAULT_FACE = "大众";
    private static final String DEFAULT_CLOTHE = "便装";
 
    private String name;    // 角色名,必填
    private int sex = DEFAULT_SEX;  // 性别:0-男、1-女
    private String face = DEFAULT_FACE;
    private String clothe = DEFAULT_CLOTHE;
 
    public Role build() {
        // 将校验逻辑放到这里,必填项、约束条件等,只有都通过了才构建对象
        if(name == null || name.isEmpty()) { return null; }
        return new Role(this);
    }
 
    public String getName() { return name; }
    public int getSex() { return sex; }
    public String getFace() { return face; }
    public String getClothe() { return clothe; }
 
    // 也可以在set方法中进行逻辑校验
    public Builder setName(String name) { this.name = name; return this; }
    public Builder setSex(int sex) { this.sex = sex; return this; }
    public Builder setFace(String face) { this.face = face; return this; }
    public Builder setClothe(String clothe) { this.clothe = clothe; return this; }
}
 
// 测试用例
public class Game {
    public static void main(String[] args) {
        Role role = new Builder().setName("杰哥").setFace("超勇").build();
    }
}

1.4 特点

优点

  • 满足开闭原则 (建造者们都相对独立,替换/新增方便)
  • 分离创建和使用 (调用方无需了解内部细节,通过统一方法接口调用即可组合成不同的对象实例)
  • 可以自由的组合对象的创建过程 (将复杂的创建步骤拆解为单个独立的创建步骤,可自由拼接)

缺点

  • 代码量增加 (属性写两份,类 Double)
  • 使用范围有限 (对象存在较多共同点,如果对象实例间差异太大,就不适合使用 Builder 模式了)
  • 容易引起超大类

1.5 和工厂模式的区别

  • 建造者模式 → 用于创建 ** 一种类型 ** 的复杂对象,通过设置不同的可选参数,定制化地创建不同的对象;
  • 工厂模式 → 用于创建 ** 不同但是类型相关 ** 的对象,由给定的参数来决定创建哪种类型的对象;

顾客进一家餐馆点餐,利用工厂模式,根据用户的不同选择,来制作不同的事物,如披萨、汉堡、沙拉。对于披萨来说,用户有各种配料可以定制,如芝士、西红柿、起司等,我们通过建造者模式根据用户选择的不同配料来制作披萨。

1.6 优缺点

优点:

  • 你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时,你可以复用相同的制造代码。
  • 单一职责原则。你可以将复杂构造代码从产品的业务逻辑中分离出来。

缺点:

  • 由于该模式需要新增多个类,因此代码整体复杂程度会有所增加。

1.7 应用场景

场景 1:避免重叠构造器的出现

class Pizza {
    Pizza(int size) { ... }
    Pizza(int size, boolean cheese) { ... }
    Pizza(int size, boolean cheese, boolean pepperoni) { ... }

场景 2:创建大致相同,具体细节不同的产品

基本生成器接口中定义了所有可能的制造步骤,具体生成器将实现这些步骤来制造特定形式的产品。同时,主管类将负责管理制造步骤的顺序。

场景 3:分步骤构建事物

你可以延迟执行某些步骤而不会影响最终产品。