建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。用户只需指定需要建造的类型就可以获得对象,建造过程及细节不需要了解。

介绍

意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用:一些基本部件不会变,而其组合经常变化的时候。

如何解决:将变与不变分离开。

关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的”套餐”。 2、JAVA 中的 StringBuilder。3、Lombok中的Builder注解

建造者(Builder)模式包含如下角色

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
  • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
  • 产品类(Product):要创建的复杂对象。
  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

常规写法

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
//产品类 电脑
@Data
public class Computer {
private String motherboard;
private String cpu;
private String memory;
private String disk;
private String gpu;
private String power;
private String heatSink;
private String chassis;
}
// 抽象 builder类(接口) 组装电脑
public interface ComputerBuilder {
Computer computer = new Computer();
void buildMotherboard();
void buildCpu();
void buildMemory();
void buildDisk();
void buildGpu();
void buildHeatSink();
void buildPower();
void buildChassis();
Computer build();
}
// 具体 builder类 华硕ROG全家桶电脑
public class AsusComputerBuilder implements ComputerBuilder {
@Override
public void buildMotherboard() {
computer.setMotherboard("Extreme主板");
}
@Override
public void buildCpu() {
computer.setCpu("Inter 12900KS");
}
@Override
public void buildMemory() {
computer.setMemory("芝奇幻峰戟 16G*2");
}
@Override
public void buildDisk() {
computer.setDisk("三星980Pro 2T");
}
@Override
public void buildGpu() {
computer.setGpu("华硕3090Ti 水猛禽");
}
@Override
public void buildHeatSink() {
computer.setHeatSink("龙神二代一体式水冷");
}
@Override
public void buildPower() {
computer.setPower("雷神二代1200W");
}
@Override
public void buildChassis() {
computer.setChassis("太阳神机箱");
}
@Override
public Computer build() {
return computer;
}
}
// 指挥者类 指挥该组装什么电脑
@AllArgsConstructor
public class ComputerDirector {
private ComputerBuilder computerBuilder;
public Computer construct() {
computerBuilder.buildMotherboard();
computerBuilder.buildCpu();
computerBuilder.buildMemory();
computerBuilder.buildDisk();
computerBuilder.buildGpu();
computerBuilder.buildHeatSink();
computerBuilder.buildPower();
computerBuilder.buildChassis();
return computerBuilder.build();
}
}
// 测试
public static void main(String[] args) {
ComputerDirector computerDirector = new ComputerDirector(new AsusComputerBuilder());
// Computer(motherboard=Extreme主板, cpu=Inter 12900KS, memory=芝奇幻峰戟 16G*2, disk=三星980Pro 2T, gpu=华硕3090Ti 水猛禽, power=雷神二代1200W, heatSink=龙神二代一体式水冷, chassis=太阳神机箱)
System.out.println(computerDirector.construct());
}

简化写法(指挥构建一体化)

上述示例代码是构建者的常规的用法,指挥类(ComputerDirector)指挥类的构建过程的先后循序,并向调用者返回完成的产品类,但是在有些情况下需要简化系统结构,可以把指挥类和抽象构建者相互结合,于是就有了下面的写法:

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
// 把指挥者类和抽象建造者合在一起的简化建造者类
public class SimpleComputerBuilder {
private Computer computer = new Computer();
public void buildMotherBoard(String motherBoard){
computer.setMotherboard(motherBoard);
}
public void buildCpu(String cpu){
computer.setCpu(cpu);
}
public void buildMemory(String memory){
computer.setMemory(memory);
}
public void buildDisk(String disk){
computer.setDisk(disk);
}
public void buildGpu(String gpu){
computer.setGpu(gpu);
}
public void buildPower(String power){
computer.setPower(power);
}
public void buildHeatSink(String heatSink){
computer.setHeatSink(heatSink);
}
public void buildChassis(String chassis){
computer.setChassis(chassis);
}
public Computer build(){
return computer;
}
}
// 测试
public static void main(String[] args) {
SimpleComputerBuilder simpleComputerBuilder = new SimpleComputerBuilder();
simpleComputerBuilder.buildMotherBoard("Extreme主板");
simpleComputerBuilder.buildCpu("Inter 12900K");
simpleComputerBuilder.buildMemory("芝奇幻峰戟 16G*2");
simpleComputerBuilder.buildDisk("三星980Pro 2T");
simpleComputerBuilder.buildGpu("华硕3090Ti 水猛禽");
simpleComputerBuilder.buildPower("雷神二代1200W");
simpleComputerBuilder.buildHeatSink("龙神二代一体式水冷");
simpleComputerBuilder.buildChassis("太阳神机箱");
// Computer(motherboard=Extreme主板, cpu=Inter 12900K, memory=芝奇幻峰戟 16G*2, disk=三星980Pro 2T, gpu=华硕3090Ti 水猛禽, power=雷神二代1200W, heatSink=龙神二代一体式水冷, chassis=太阳神机箱)
System.out.println(simpleComputerBuilder.build());
}

可以看到,对比常规写法,这样写确实简化了系统结构,但同时也加重了建造者类的职责,也不是太符合单一职责原则,如果construct() 过于复杂,建议还是封装到 Director 中。

链式写法

Lombok、Mybatis-plus

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
// 链式写法建造者类
public class SimpleComputerBuilder {
private Computer computer = new Computer();
public SimpleComputerBuilder buildMotherBoard(String motherBoard){
computer.setMotherboard(motherBoard);
return this;
}
public SimpleComputerBuilder buildCpu(String cpu){
computer.setCpu(cpu);
return this;
}
public SimpleComputerBuilder buildMemory(String memory){
computer.setMemory(memory);
return this;
}
public SimpleComputerBuilder buildDisk(String disk){
computer.setDisk(disk);
return this;
}
public SimpleComputerBuilder buildGpu(String gpu){
computer.setGpu(gpu);
return this;
}
public SimpleComputerBuilder buildPower(String power){
computer.setPower(power);
return this;
}
public SimpleComputerBuilder buildHeatSink(String heatSink){
computer.setHeatSink(heatSink);
return this;
}
public SimpleComputerBuilder buildChassis(String chassis){
computer.setChassis(chassis);
return this;
}
public Computer build(){
return computer;
}
}
// 测试
public static void main(String[] args) {
Computer asusComputer = new SimpleComputerBuilder().buildMotherBoard("Extreme主板")
.buildCpu("Inter 12900K")
.buildMemory("芝奇幻峰戟 16G*2")
.buildDisk("三星980Pro 2T")
.buildGpu("华硕3090Ti 水猛禽")
.buildPower("雷神二代1200W")
.buildHeatSink("龙神二代一体式水冷")
.buildChassis("太阳神机箱").build();
System.out.println(asusComputer);
}

可以看到,其实链式写法与普通写法的区别并不大,只是在建造者类组装部件的时候,同时将建造者类返回即可,使用链式写法使用起来更方便,某种程度上也可以提高开发效率。从软件设计上,对程序员的要求比较高。

总结

适用场景:

  • 适用于创建对象需要很多步骤,但是步骤顺序不一定固定。
  • 如果一个对象有非常复杂的内部结构(属性),把复杂对象的创建和使用进行分离。

优点:

  • 封装性好,创建和使用分离。
  • 扩展性好,建造类之间独立、一定程度上解耦。

缺点:

  • 产生多余的Builder对象。
  • 产品内部发生变化,建造者都要修改,成本较大。

与工厂模式的区别:

  • 建造者模式更注重方法的调用顺序,工厂模式更注重创建对象。
  • 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。
  • 关注点不同,工厂模式只需要把对象创建出来就可以了,而建造者模式中不仅要创建出这个对象,还要知道这个对象由哪些部件组成。
  • 建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。

与抽象工厂模式的区别:

  • 抽象工厂模式实现对产品族的创建,一个产品族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
  • 建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
  • 建造者模式所有函数加到一起才能生成一个对象,抽象工厂一个函数生成一个对象