建造者模式(Builder),又叫生成器模式,是一种对象构建模式。它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式可以将一个产品的内部表象和产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。
建造者模式解析
角色介绍
- Product(产品角色):具体产品对象。
- Builder(抽象建造者):是为创建一个 Product 对象的各个部件指定的抽象接口。
- ConcreteBuilder(具体建造者):实现 Builder 接口,构建和装配各个部件。
- Director(指挥者):是构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。 它主要有两个作用,一是隔离了客户与对象的生产过程,二是负责控制产品对象的生产过程。
建造者模式基本代码
- Product 类:产品类,由多个部件组成。
1 | public class Product { |
2 | |
3 | private List<String> parts = new ArrayList<>(); |
4 | |
5 | /** |
6 | * 添加部件 |
7 | */ |
8 | public void add(String part) { |
9 | this.parts.add(part); |
10 | } |
11 | |
12 | /** |
13 | * 列举所有部件 |
14 | */ |
15 | public void show() { |
16 | this.parts.forEach(System.out::println); |
17 | } |
18 | } |
- Builder 类:抽象建造者类,确定产品由两个部件 PartA 和 PartB 组成,并声明一个得到产品结果的方法 getResult()。
1 | public interface Builder { |
2 | |
3 | void buildPartA(); |
4 | |
5 | void buildPartB(); |
6 | |
7 | Product getResult(); |
8 | } |
- ConcreteBuilderA 类:具体建造者 A。
1 | public class ConcreteBuilderA implements Builder { |
2 | |
3 | private Product product = new Product(); |
4 | |
5 | |
6 | public void buildPartA() { |
7 | product.add("X"); |
8 | } |
9 | |
10 | |
11 | public void buildPartB() { |
12 | product.add("Y"); |
13 | } |
14 | |
15 | |
16 | public Product getResult() { |
17 | return product; |
18 | } |
19 | } |
- ConcreteBuilderB 类:具体建造者 B。
1 | public class ConcreteBuilderB implements Builder { |
2 | |
3 | private Product product = new Product(); |
4 | |
5 | |
6 | public void buildPartA() { |
7 | product.add("M"); |
8 | } |
9 | |
10 | |
11 | public void buildPartB() { |
12 | product.add("N"); |
13 | } |
14 | |
15 | |
16 | public Product getResult() { |
17 | return product; |
18 | } |
19 | } |
- Director 类:指挥者类。
1 | public class Director { |
2 | |
3 | public void build(Builder builder) { |
4 | builder.buildPartA(); |
5 | builder.buildPartB(); |
6 | } |
7 | } |
- Main 类
1 | public class Main { |
2 | |
3 | public static void main(String[] args) { |
4 | Director director = new Director(); |
5 | |
6 | ConcreteBuilderA builderA = new ConcreteBuilderA(); |
7 | ConcreteBuilderB builderB = new ConcreteBuilderB(); |
8 | |
9 | director.build(builderA); |
10 | Product resultA = builderA.getResult(); |
11 | resultA.show(); |
12 | |
13 | director.build(builderB); |
14 | Product resultB = builderB.getResult(); |
15 | resultB.show(); |
16 | } |
17 | } |
示例
需求:盖房子,过程有打桩、砌墙、封顶。房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不相同的。
传统写法
1 | /** |
2 | * House |
3 | */ |
4 | public class House { |
5 | |
6 | private Integer length; |
7 | |
8 | private Integer width; |
9 | |
10 | private Integer high; |
11 | |
12 | public Integer getLength() { |
13 | return length; |
14 | } |
15 | |
16 | public void setLength(Integer length) { |
17 | this.length = length; |
18 | } |
19 | |
20 | public Integer getWidth() { |
21 | return width; |
22 | } |
23 | |
24 | public void setWidth(Integer width) { |
25 | this.width = width; |
26 | } |
27 | |
28 | public Integer getHigh() { |
29 | return high; |
30 | } |
31 | |
32 | public void setHigh(Integer high) { |
33 | this.high = high; |
34 | } |
35 | |
36 | |
37 | public String toString() { |
38 | return "House{" + |
39 | "length=" + length + |
40 | ", width=" + width + |
41 | ", high=" + high + |
42 | '}'; |
43 | } |
44 | } |
45 | |
46 | /** |
47 | * 普通房子 |
48 | */ |
49 | public class CommonHouseBuilder { |
50 | |
51 | public House build() { |
52 | House house = new House(); |
53 | house.setLength(50); |
54 | house.setWidth(20); |
55 | house.setHigh(5); |
56 | |
57 | return house; |
58 | } |
59 | } |
60 | |
61 | /** |
62 | * 高房子 |
63 | */ |
64 | public class HighHouseBuilder { |
65 | |
66 | public House build() { |
67 | House house = new House(); |
68 | house.setLength(60); |
69 | house.setWidth(30); |
70 | house.setHigh(6); |
71 | |
72 | return house; |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * Main |
78 | */ |
79 | public class Main { |
80 | |
81 | public static void main(String[] args) { |
82 | // 普通房子 |
83 | CommonHouseBuilder commonHouseBuilder = new CommonHouseBuilder(); |
84 | House commonHouse = commonHouseBuilder.build(); |
85 | System.out.println(commonHouse.toString()); |
86 | |
87 | // 高房子 |
88 | HighHouseBuilder highHouseBuilder = new HighHouseBuilder(); |
89 | House highHouse = highHouseBuilder.build(); |
90 | System.out.println(highHouse.toString()); |
91 | |
92 | } |
93 | } |
上面这种写法将建造过程和表象都放在了一起,耦合性很强,而且普通房子和高房子的建造过程是一样的,只是内部的细节不同,代码是可以复用的。如果我们又要进行别墅的建造,建造过程我们又要重新写一遍,而且很容易漏写步骤。所以让我们使用建造者模式改进。
使用建造者模式改进
1 | /** |
2 | * House |
3 | */ |
4 | public class House { |
5 | |
6 | private Integer length; |
7 | |
8 | private Integer width; |
9 | |
10 | private Integer high; |
11 | |
12 | public Integer getLength() { |
13 | return length; |
14 | } |
15 | |
16 | public void setLength(Integer length) { |
17 | this.length = length; |
18 | } |
19 | |
20 | public Integer getWidth() { |
21 | return width; |
22 | } |
23 | |
24 | public void setWidth(Integer width) { |
25 | this.width = width; |
26 | } |
27 | |
28 | public Integer getHigh() { |
29 | return high; |
30 | } |
31 | |
32 | public void setHigh(Integer high) { |
33 | this.high = high; |
34 | } |
35 | |
36 | |
37 | public String toString() { |
38 | return "House{" + |
39 | "length=" + length + |
40 | ", width=" + width + |
41 | ", high=" + high + |
42 | '}'; |
43 | } |
44 | } |
45 | |
46 | /** |
47 | * Builder |
48 | */ |
49 | public interface Builder { |
50 | |
51 | void buildPartA(); |
52 | |
53 | void buildPartB(); |
54 | |
55 | Product getResult(); |
56 | } |
57 | |
58 | /** |
59 | * 普通房子建造者 |
60 | */ |
61 | public class CommonHouseBuilder implements HouseBuilder { |
62 | |
63 | private House house = new House(); |
64 | |
65 | |
66 | public void buildHigh() { |
67 | house.setHigh(5); |
68 | } |
69 | |
70 | |
71 | public void buildWidth() { |
72 | house.setWidth(20); |
73 | } |
74 | |
75 | |
76 | public void buildLength() { |
77 | house.setLength(50); |
78 | } |
79 | |
80 | |
81 | public House getResult() { |
82 | return house; |
83 | } |
84 | } |
85 | |
86 | /** |
87 | * 高房子建造者 |
88 | */ |
89 | public class HighHouseBuilder implements HouseBuilder { |
90 | |
91 | private House house = new House(); |
92 | |
93 | |
94 | public void buildHigh() { |
95 | house.setHigh(6); |
96 | } |
97 | |
98 | |
99 | public void buildWidth() { |
100 | house.setWidth(30); |
101 | } |
102 | |
103 | |
104 | public void buildLength() { |
105 | house.setLength(60); |
106 | } |
107 | |
108 | |
109 | public House getResult() { |
110 | return house; |
111 | } |
112 | } |
113 | |
114 | /** |
115 | * HouseDirector |
116 | */ |
117 | public class HouseDirector { |
118 | |
119 | public void buildHouse(HouseBuilder houseBuilder) { |
120 | houseBuilder.buildHigh(); |
121 | houseBuilder.buildLength(); |
122 | houseBuilder.buildWidth(); |
123 | } |
124 | } |
125 | |
126 | /** |
127 | * Main |
128 | */ |
129 | public class Main { |
130 | |
131 | public static void main(String[] args) { |
132 | HouseDirector houseDirector = new HouseDirector(); |
133 | // 普通房子 |
134 | CommonHouseBuilder commonHouseBuilder = new CommonHouseBuilder(); |
135 | houseDirector.buildHouse(commonHouseBuilder); |
136 | House commonHouse = commonHouseBuilder.getResult(); |
137 | System.out.println(commonHouse.toString()); |
138 | |
139 | // 高房子 |
140 | HighHouseBuilder highHouseBuilder = new HighHouseBuilder(); |
141 | houseDirector.buildHouse(highHouseBuilder); |
142 | House highHouse = highHouseBuilder.getResult(); |
143 | System.out.println(highHouse.toString()); |
144 | |
145 | } |
146 | } |
小结
建造者模式,主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临复杂的变化。而建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品的构建过程,所以如果要改变一个产品内部的表示,只需再定义一个具体的建造者就可以了。
最后,建造者模式是逐步建造产品的,所以建造者的 Builder 类里的那些建造方法必须是足够普遍的,以便为各类型的具体建造者构造。如果产品的差异性较大,就不适合使用建造者模式。