设计模式之建造者模式

简介

建造者模式是一种创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。 简单来说,就是将对象的构建过程封装起来,允许用户分步骤地构建复杂对象,而无需了解其内部构造细节。
与工厂模式不同,建造者模式侧重于逐步构建一个复杂的对象,而不是一次性创建。 建造者模式关注的是对象各部分的组装过程,而工厂模式关注的是创建整个对象。

建造者模式的角色

建造者模式通常包含以下几个角色:

  1. 抽象建造者 (Builder): 定义一个抽象接口,规范产品对象的各个组成部分的建造方法。 这个接口声明了要创建复杂对象的哪些部分,但不涉及具体对象部件的创建。
  2. 具体建造者 (Concrete Builder): 实现 Builder 接口,针对不同的商业逻辑,具体化复杂对象的各个部分的创建。 在建造过程完成后,提供产品的实例。
  3. 指挥者 (Director): 调用具体建造者来创建复杂对象的各个部分,控制对象各部分的组装顺序。 指挥者不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。 指挥者是可选的,如果构建过程足够简单,可以直接由客户端来调用具体建造者。
  4. 产品 (Product): 要创建的复杂对象。

建造者模式的使用场景

以下是一些适合使用建造者模式的场景:

  1. 需要生成的对象具有复杂的内部结构: 当一个对象包含多个部件,且这些部件的创建逻辑复杂时,可以使用建造者模式将构建过程分解为多个步骤。
  2. 需要生成的对象内部属性本身相互依赖: 当对象的某些属性依赖于其他属性时,可以使用建造者模式来确保属性之间的正确初始化。
  3. 需要控制对象的构建过程: 当需要对对象的构建过程进行精细控制,例如指定构建顺序或添加验证逻辑时,可以使用建造者模式。
  4. 创建不同表示的对象:当相同的构建过程需要生成不同类型的对象时,可以使用不同的具体建造者。

建造者模式的优点

  • 封装性: 隐藏了产品内部的构造细节。
  • 灵活性: 可以灵活地指定产品的各个部件。
  • 可读性: 链式调用使代码更易读。
  • 可扩展性: 可以很容易地添加新的建造者类,以构建不同类型的产品。
  • 控制: 可以在 build() 方法中添加验证逻辑,保证产品对象的有效性。
  • 易于维护: 将复杂的构建过程分解为多个步骤,使得代码更易于理解和维护。

建造者模式与工厂模式的区别

  • 关注点不同: 建造者模式侧重于逐步构建一个复杂的对象,而工厂模式侧重于一次性创建对象。
  • 复杂度不同: 建造者模式通常用于构建更复杂的对象,而工厂模式可以用于创建相对简单的对象。
  • 灵活性不同: 建造者模式可以更灵活地控制对象的构建过程,而工厂模式的灵活性相对较低。

示例

Java 中的 StringBuilder 就是建造者模式的一个典型应用。它将单个字符或字符串逐步组装成最终的字符串。

以下是一个使用建造者模式构建电脑的示例:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// 1. 产品类 (Product)
class Computer {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
private String display;

// 私有构造器,强制使用建造者模式
private Computer(String cpu, String ram, String storage, String graphicsCard, String display) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
this.graphicsCard = graphicsCard;
this.display = display;
}

//这里忽略getter和setter方法

// 重写 toString 方法,方便打印
@Override
public String toString() {
return "电脑{" +
"CPU='" + cpu + '\'' +
", RAM='" + ram + '\'' +
", 储存='" + storage + '\'' +
", 显卡='" + graphicsCard + '\'' +
", 显示器='" + display + '\'' +
'}';
}


// 2. 建造者接口 (Builder Interface) - 也可以是一个抽象类
public interface Builder {
Builder cpu(String cpu);
Builder ram(String ram);
Builder storage(String storage);
Builder graphicsCard(String graphicsCard);
Builder display(String display);
Computer build(); // 构建产品的方法
}


// 3. 具体建造者类 (Concrete Builder)
public static class ComputerBuilder implements Builder {
private String cpu;
private String ram;
private String storage;
private String graphicsCard;
private String display;

@Override
public ComputerBuilder cpu(String cpu) {
this.cpu = cpu;
return this;
}

@Override
public ComputerBuilder ram(String ram) {
this.ram = ram;
return this;
}

@Override
public ComputerBuilder storage(String storage) {
this.storage = storage;
return this;
}

@Override
public ComputerBuilder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}

@Override
public ComputerBuilder display(String display) {
this.display = display;
return this;
}

@Override
public Computer build() {
// 可在此处添加验证逻辑,例如,必须有 CPU 和 RAM
if (cpu == null || ram == null) {
throw new IllegalStateException("组装电脑,CPU和RAM是必需的。");
}
return new Computer(cpu, ram, storage, graphicsCard, display);
}
}
}


// 4. 指挥者类 (Director) - 可选
class ComputerDirector {
private Computer.Builder builder;

public ComputerDirector(Computer.Builder builder) {
this.builder = builder;
}

public Computer constructGamingComputer() {
return builder.cpu("Intel i9").ram("32GB").storage("1TB SSD").graphicsCard("Nvidia RTX 5070").display("27-inch 4K").build();
}

public Computer constructOfficeComputer() {
return builder.cpu("Intel i5").ram("16GB").storage("512GB SSD").display("24-inch 1080p").build(); // 缺少 graphicsCard 是可以的
}
}



// 5. 客户端代码 (Client)
public class BuilderDemo {
public static void main(String[] args) {
// 使用建造者模式

// 方法一:直接使用 Builder
Computer computer1 = new Computer.ComputerBuilder()
.cpu("AMD Ryzen 5")
.ram("16GB")
.storage("500GB SSD")
.graphicsCard("AMD Radeon RX 7700")
.display("24 inch")
.build();

System.out.println("电脑 1: " + computer1);

// 方法二:使用 Director,更灵活
Computer.Builder builder = new Computer.ComputerBuilder();
ComputerDirector director = new ComputerDirector(builder);
Computer gamingComputer = director.constructGamingComputer();
System.out.println("游戏电脑: " + gamingComputer);

Computer officeComputer = director.constructOfficeComputer();
System.out.println("办公电脑: " + officeComputer);


// 测试验证逻辑
try {
Computer invalidComputer = new Computer.ComputerBuilder().storage("1TB").build(); // 缺少 CPU 和 RAM
System.out.println(invalidComputer); // 不应该执行到这里
} catch (IllegalStateException e) {
System.out.println("出现异常: " + e.getMessage()); // 打印异常信息
}
}
}

关键点说明:

  • 产品类 (Product): Computer 类是最终要创建的复杂对象。 注意它的构造函数是私有的,这样就只能通过 Builder 来创建它,保证了建造过程的控制。
  • 建造者接口 (Builder Interface): Computer.Builder 接口定义了创建 Computer 对象各个部分的方法。 它也可以是一个抽象类,如果有一些通用的构建逻辑。
  • 具体建造者 (Concrete Builder): Computer.ComputerBuilder 实现了 Computer.Builder 接口,负责具体的产品构建过程。 它提供链式调用的方式来设置各个属性,并且 build() 方法返回最终的 Computer 对象。
  • 指挥者 (Director) - 可选: ComputerDirector 类负责管理建造者的构建顺序。 它封装了复杂的构建逻辑,客户端只需要选择构建哪种类型的 Computer,而不需要关心构建细节。 如果构建过程很简单,Director 可以省略。
  • 客户端 (Client): 客户端代码创建建造者或指挥者,并调用相应的方法来构建 Computer 对象。 客户端无需关心 Computer 对象的构建细节。

运行 BuilderDemo.java 将会打印出:

1
2
3
4
电脑 1: 电脑{CPU='AMD Ryzen 5', RAM='16GB', 储存='500GB SSD', 显卡='AMD Radeon RX 7700', 显示器='24 inch'}
游戏电脑: 电脑{CPU='Intel i9', RAM='32GB', 储存='1TB SSD', 显卡='Nvidia RTX 5070', 显示器='27-inch 4K'}
办公电脑: 电脑{CPU='Intel i5', RAM='16GB', 储存='512GB SSD', 显卡='null', 显示器='24-inch 1080p'}
出现异常: 组装电脑,CPU和RAM是必需的。

总结

建造者模式是一种非常有用的创建型设计模式,可以帮助我们构建复杂的对象,并控制对象的构建过程。 它可以提高代码的可读性、可维护性和可扩展性。 在实际开发中,我们可以根据具体情况选择是否使用建造者模式,以及如何使用它。