建造者模式

11.1 变化是永恒的

又是一个周三,快要下班了,老大突然拉住我,喜滋滋地告诉我:“牛叉公司很满意我们做的模型,又签订了一个合同,把奔驰、宝马的车辆模型都交给我们公司制 作了,不过这次又额外增加了一个新需求:汽车的启动、停止、喇叭声音、引擎声音都由客户自己控制,他想什么顺序就什么顺序,这个没问题吧?”

看着老大殷切的目光,我还能说啥?非常肯定地点头,“没问题!”,加班加点做呗,“再苦再累就当自己二百五!再难再险就当自己二皮脸!与君共勉!”这句话说出了俺的心声。

那任务是接下来,又是一个时间紧,工程量大的项目,为什么是“又”呢?因为基本上每个项目都是如此,我该怎么来完成这个任务呢?

首先我们分析一下需求,奔驰、宝马都是一个产品,他们有共有的属性,牛叉公司关心的是单个模型的运行过程:奔驰模型A是先有引擎声音,然后再响喇叭;奔驰 模型B是先启动起来,然后再有引擎声音,这才是牛叉公司要关心的,那到我们老大这边呢,就是满足人家的要求,要什么顺序就立马能产生什么顺序的模型出来, 我就负责把老大的要求实现出来,而且还要是批量的,也就是说牛叉公司下单订购宝马A车模,我们老大马上就找我“生产一个这样的车模,启动完毕后,喇叭响一 下”,然后我们就准备开始批量生产这些模型。由我生产出N多个奔驰和宝马车辆模型,这些车辆模型的都有run()方法,但是具体到每一个模型的run() 方法中间的执行任务的顺序是不同的,老大说要啥顺序,我就给啥顺序,最终客户买走后只能是既定的模型。好,需求还是比较复杂,我们先一个一个的解决,先从 找一个最简单的切入点——产品类,每个车都是一个产品,如图11-1所示。

图11-1 汽车模型类图

类图比较简单,在CarModel中我们定义了一个setSequence方法,车辆模型的这几个动作要如何排布,是在这个ArrayList中定义的, 然后run()方法根据sequence定义的顺序完成指定的顺序动作,与我们上一章节介绍的模板方法模式是不是非常类似?好,我们先看CarModel 源代码,如代码清单11-1所示。

代码清单11-1 车辆模型的抽象类

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
public abstract class CarModel {
 
//这个参数是各个基本方法执行的顺序
 
private ArrayList<String> sequence = new ArrayList<String>();
 
//模型是启动开始跑了
 
protected abstract void start();
 
//能发动,那还要能停下来,那才是真本事
 
protected abstract void stop();
 
//喇叭会出声音,是滴滴叫,还是哔哔叫
 
protected abstract void alarm();
 
//引擎会轰隆隆地响,不响那是假的
 
protected abstract void engineBoom();
 
//那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
 
final public void run() {
 
//循环一边,谁在前,就先执行谁
 
for(int i=0;i<this.sequence.size();i++){
 
String actionName = this.sequence.get(i);
 
if(actionName.equalsIgnoreCase("start")){
 
this.start(); //开启汽车
 
}else if(actionName.equalsIgnoreCase("stop")){
 
this.stop(); //停止汽车
 
}else if(actionName.equalsIgnoreCase("alarm")){
 
this.alarm(); //喇叭开始叫了
 
}else if(actionName.equalsIgnoreCase("engine boom")){ //如果是engine boom关键字
 
this.engineBoom(); //引擎开始轰鸣
 
}
 
}
 
}
 
//把传递过来的值传递到类内
 
final public void setSequence(ArrayList<String> sequence){
 
this.sequence = sequence;
 
}
 
}

CarModel的设计原理是这样的,setSequence方法是允许客户自己设置一个顺序,是要先启动响一下喇叭再跑起来,还是要先响一下喇叭 再启动,对于一个具体的模型永远都固定的,但是对N多个模型就是动态的了。在子类中实现父类的基本方法,run()方法读取sequence,然后遍历 sequence中的字符串,哪个字符串在先,就先执行哪个方法。

两个实现类分别实现父类的基本方法,奔驰模型如代码清单11-2所示。

代码清单11-2 奔驰模型代码

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
public class BenzModel extends CarModel {
 
protected void alarm() {
 
System.out.println("奔驰车的喇叭声音是这个样子的...");
 
}
 
protected void engineBoom() {
 
System.out.println("奔驰车的引擎室这个声音的...");
 
}
 
protected void start() {
 
System.out.println("奔驰车跑起来是这个样子的...");
 
}
 
protected void stop() {
 
System.out.println("奔驰车应该这样停车...");
 
}
 
}

宝马车模型如代码清单11-3所示。

代码清单11-3 宝马模型代码

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
public class BMWModel extends CarModel {
 
protected void alarm() {
 
System.out.println("宝马车的喇叭声音是这个样子的...");
 
}
 
protected void engineBoom() {
 
System.out.println("宝马车的引擎室这个声音的...");
 
}
 
protected void start() {
 
System.out.println("宝马车跑起来是这个样子的...");
 
}
 
protected void stop() {
 
System.out.println("宝马车应该这样停车...");
 
}
 
}

两个产品的实现类都完成,我们来模拟一下牛叉公司的要求:生产1件奔驰模型,要求跑的时候,先发动引擎,然后再挂档启动,然后停下来,不需要喇叭。这个需求很容易满足,我们增加一个场景类实现该需求,如代码清单11-4所示。

代码清单11-4 宝马模型代码

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
public class Client {
 
public static void main(String[] args) {
 
/*
 
* 客户告诉牛叉公司,我要这样一个模型,然后牛叉公司就告诉我老大
 
* 说要这样一个模型,这样一个顺序,然后我就来制造
 
*/
 
BenzModel benz = new BenzModel();
 
//存放run的顺序
 
ArrayList<String> sequence = new ArrayList<String>();
 
sequence.add("engine boom"); //客户要求,run的时候时候先发动引擎
 
sequence.add("start"); //启动起来
 
sequence.add("stop"); //开了一段就停下来
 
//我们把这个顺序赋予奔驰车
 
benz.setSequence(sequence);
 
benz.run();
 
}
 
}

运行结果如下所示。

奔驰车的引擎是这个声音的...

奔驰车跑起来是这个样子的...

奔驰车应该这样停车...

看,我们组装了这样的一辆汽车,满足了牛叉公司的需求了。但是想想我们的需求,汽车的动作执行顺序是要能够随意调整的,我们只满足了一个需求,还要下一个 需求呀,然后是第2件宝马模型,只要启动、停止,其他的什么都不要,第3件模型,先喇叭,然后启动,然后停止,第4件...直到把你逼疯为止,那怎么办? 我们就一个一个的来写场景类满足吗?不可能了,那我们要想办法来解决这个问题,有了!我们为每种模型产品模型定义一个建造者,你要啥顺序直接告诉建造者, 由建造者来建造,于是乎我们就有了如图11-2所示的类图。

图11-2 增加了建造者的汽车模型类图

增加了一个CarBuilder抽象类,由它来组装各个车模,要什么类型什么顺序的车辆模型,都由相关的子类完成,首先编写CarBuilder代码,如代码清单11-5所示。

代码清单11-5 抽象汽车组装者

1
2
3
4
5
6
7
8
9
10
11
public abstract class CarBuilder {
 
//建造一个模型,你要给我一个顺序要,就是组装顺序
 
public abstract void setSequence(ArrayList<String> sequence);
 
//设置完毕顺序后,就可以直接拿到这个车辆模型
 
public abstract CarModel getCarModel();
 
}

很简单,每个车辆模型都要有确定的运行顺序,然后才能返回一个车辆模型。奔驰车的组装者如代码清单11-6所示。

代码清单11-6 奔驰车组装者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BenzBuilder extends CarBuilder {
 
private BenzModel benz = new BenzModel();
 
public CarModel getCarModel() {
 
return this.benz;
 
}
 
public void setSequence(ArrayList<String> sequence) {
 
this.benz.setSequence(sequence);
 
}
 
}

非常简单实用的程序,给定一个汽车的运行顺序,然后就返回一个奔驰车,简单了很多,宝马车的组装与此相同,如代码清单11-7所示。

代码清单11-7 宝马车组装者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BMWBuilder extends CarBuilder {
 
private BMWModel bmw = new BMWModel();
 
public CarModel getCarModel() {
 
return this.bmw;
 
}
 
public void setSequence(ArrayList<String> sequence) {
 
this.bmw.setSequence(sequence);
 
}
 
}

两个组装者都完成了,我们再来看看牛叉公司的需求如何满足,修改一下场景类,如代码清单11-8所示。

代码清单11-8 修改后的场景类

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
public class Client {
 
public static void main(String[] args) {
 
/*
 
* 客户告诉牛叉公司,我要这样一个模型,然后牛叉公司就告诉我老大
 
* 说要这样一个模型,这样一个顺序,然后我就来制造
 
*/
 
ArrayList<String> sequence = new ArrayList<String>(); //存放run的顺序
 
sequence.add("engine boom"); //客户要求,run的时候时候先发动引擎
 
sequence.add("start"); //启动起来
 
sequence.add("stop"); //开了一段就停下来
 
//要一个奔驰车:
 
BenzBuilder benzBuilder = new BenzBuilder();
 
//把顺序给这个builder类,制造出这样一个车出来
 
benzBuilder.setSequence(sequence);
 
//制造出一个奔驰车
 
BenzModel benz = (BenzModel)benzBuilder.getCarModel();
 
//奔驰车跑一下看看
 
benz.run();
 
}
 
}

运行结果如下所示。

奔驰车的引擎是这个声音的...

奔驰车跑起来是这个样子的...

奔驰车应该这样停车...

那如果我再想要个同样顺序的宝马车呢?很简单,再次修改一下场景类,如代码清单11-9所示。

代码清单11-9 相同顺序的宝马车的场景类

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
public class Client {
 
public static void main(String[] args) {
 
ArrayList<String> sequence = new ArrayList<String>(); //存放run的顺序
 
sequence.add("engine boom"); //客户要求,run的时候时候先发动引擎
 
sequence.add("start"); //启动起来
 
sequence.add("stop"); //开了一段就挺下来
 
//要一个奔驰车:
 
BenzBuilder benzBuilder = new BenzBuilder();
 
//把顺序给这个builder类,制造出这样一个车出来
 
benzBuilder.setSequence(sequence);
 
//制造出一个奔驰车
 
BenzModel benz = (BenzModel)benzBuilder.getCarModel();
 
//奔驰车跑一下看看
 
benz.run();
 
//按照同样的顺序,我再要一个宝马
 
BMWBuilder bmwBuilder = new BMWBuilder();
 
bmwBuilder.setSequence(sequence);
 
BMWModel bmw = (BMWModel)bmwBuilder.getCarModel();
 
bmw.run();
 
}
 
}

运行结果如下所示。

奔驰车的引擎是这个声音的...

奔驰车跑起来是这个样子的...

奔驰车应该这样停车...

宝马车的引擎是这个声音的...

宝马车跑起来是这个样子的...

宝马车应该这样停车...

看,同样运行顺序的宝马车也生产出来了,而且代码是不是比刚开始直接访问产品类(Procuct)简单了很多。我们在做项目时,经常会有一个共识:需求是 无底洞,是无理性的,不可能你告诉它不增加需求就不增加,这四个过程(start、stop、alarm、engineBoom)按照排列组合有很多种, 牛叉公司可以随意组合,它要什么顺序的车模我就必须生成什么顺序的车模,客户可是上帝!那我们不可能预知他们要什么顺序的模型呀,怎么办?封装一下,找一 个导演,指挥各个事件的先后顺序,然后为每种顺序指定一个代码,你说一种我们立刻就给你生产处理,好方法,厉害!我们先修正一下类图,如图11-3所示。

图11-3 完整汽车模型类图

类图看着复杂了,但是还是比较简单,我们增加了一个Director类,负责按照指定的顺序生产模型,其中方法说明如下:

  • getABenzModel方法

组建出A型号的奔驰车辆模型,其过程为只有启动(start)、停止(stop)方法,其他的引擎声音、喇叭都没有。

  • getBBenzModel方法

组建出B型号的奔驰车,其过程为先发动引擎(engine boom),然后启动(star),再然后停车(stop),没有喇叭。

  • getCBMWModel方法

组建出C型号的宝马车,其过程为先喇叭叫一下(alarm),然后(start),再然后是停车(stop),引擎不轰鸣。

  • getDBMWModel方法

组建出D型号的宝马车,其过程就一个启动(start),然后一路跑到黑,永动机,没有停止方法,没有喇叭,没有引擎轰鸣。

其他的E型号、F型号……等等,可以有很多,启动(start)、停止(stop)、喇叭(alarm)、引擎轰鸣(engine boom)这四个方法在这个类中可以随意的自由组合,有几种呢?好像是排列组合,这个不会算,高中数学没学好,反正有很多种了,都可以实现。 Director类如代码清单11-10所示。

代码清单11-10 导演类

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
public class Director {
 
private ArrayList<String> sequence = new ArrayList();
 
private BenzBuilder benzBuilder = new BenzBuilder();
 
private BMWBuilder bmwBuilder = new BMWBuilder();
 
/*
 
* A类型的奔驰车模型,先start,然后stop,其他什么引擎了,喇叭一概没有
 
*/
 
public BenzModel getABenzModel(){
 
//清理场景,这里是一些初级程序员不注意的地方
 
this.sequence.clear();
 
//这只ABenzModel的执行顺序
 
this.sequence.add("start");
 
this.sequence.add("stop");
 
//按照顺序返回一个奔驰车
 
this.benzBuilder.setSequence(this.sequence);
 
return (BenzModel)this.benzBuilder.getCarModel();
 
}
 
/*
 
* B型号的奔驰车模型,是先发动引擎,然后启动,然后停止,没有喇叭
 
*/
 
public BenzModel getBBenzModel(){
 
this.sequence.clear();
 
this.sequence.add("engine boom");
 
this.sequence.add("start");
 
this.sequence.add("stop");
 
this.benzBuilder.setSequence(this.sequence);
 
return (BenzModel)this.benzBuilder.getCarModel();
 
}
 
/*
 
* C型号的宝马车是先按下喇叭(炫耀嘛),然后启动,然后停止
 
*/
 
public BMWModel getCBMWModel(){
 
this.sequence.clear();
 
this.sequence.add("alarm");
 
this.sequence.add("start");
 
this.sequence.add("stop");
 
this.bmwBuilder.setSequence(this.sequence);
 
return (BMWModel)this.bmwBuilder.getCarModel();
 
}
 
/*
 
* D类型的宝马车只有一个功能,就是跑,启动起来就跑,永远不停止,牛叉
 
*/
 
public BMWModel getDBMWModel(){
 
this.sequence.clear();
 
this.sequence.add("start");
 
this.bmwBuilder.setSequence(this.sequence);
 
return (BMWModel)this.benzBuilder.getCarModel();
 
}
 
/*
 
* 这里还可以有很多方法,你可以先停止,然后再启动,或者一直停着不动,静态的嘛
 
* 导演类嘛,按照什么顺序是导演说了算
 
*/
 
}

顺便说一下,大家看一下程序中有很多this调用,这个我一般是这样要求项目组成员的,如果你要调用类中的成员变量或方法,需要在前面加上this关键 字,不加也能正常的跑起来,但是不清晰,加上this关键字,我就是要调用本类中成员变量或方法,而不是本方法的中的一个变量,还有super方法也是一 样,是调用父类的的成员变量或者方法,那就加上这个关键字,不要省略,这要靠约束,还有就是程序员的自觉性,他要是死不悔改,那咱也没招。

注意上面每个方法都一个this.sequence.clear(),这个估计你一看就 明白,但是作为一个系统分析师或是技术经理一定要告诉告诉项目成员,ArrayList和HashMap如果定义成类的成员变量,那你在方法中调用一定要 做一个clear的动作,防止数据混乱。如果你发生过一次类似问题的话,比如ArrayList中出现一个“出乎意料”的数据,而你又花费了几个通宵才解 决这个问题,那你会有很深刻的印象。

有了这样一个导演类后,我们的场景类就更容易处理了,牛叉公司要A类型的奔驰车1W辆,B类型的奔驰车100W辆,C类型的宝马车1000W辆,D类型的不需要,非常容易处理,如代码清单11-11所示。

代码清单11-11 导演类

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
public class Client {
 
public static void main(String[] args) {
 
Director director = new Director();
 
//1W辆A类型的奔驰车
 
for(int i=0;i<10000;i++){
 
director.getABenzModel().run();
 
}
 
//100W辆B类型的奔驰车
 
for(int i=0;i<1000000;i++){
 
director.getBBenzModel().run();
 
}
 
//1000W辆C类型的宝马车
 
for(int i=0;i<10000000;i++){
 
director.getCBMWModel().run();
 
}
 
}
 
}

清晰,简单吧,我们写程序重构的最终目的就是:简单、清晰,代码是让人看的,不是写完就完事了,我一直在教育我带的团队,Java程序不是像我们前辈写二 进制代码、汇编一样,写完基本上就自己能看懂,别人看就跟看天书一样,现在的高级语言,要像写中文汉字一样,你写的,别人能看懂。——这就是建造者模式。

11.2 建造者模式的定义

建造者模式(Builder Pattern)也叫做生成器模式,其定义如下:

Separate the construction of a complex object from its representation so that the same construction process can create different representations. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式的通用类图如图11-4所示。

图11-4 建造者模式通用类图

在建造者模式中,有如下四个角色:

  • Product 产品类

通常是实现了模板方法模式,也就是有模板方法和基本方法,这个参考上一章节的模板方法模式。在例子中,BenzModel和BMWModel就属于产品类。

  • Builder 抽象建造者

规范产品的组建,一般是由子类实现。在例子中,CarBuilder属于抽象建造者。

  • ConcreteBuilder 具体建造者

实现抽象类定义的所有方法,并且返回一个组件好的对象。在例子中,BenzBuilder和BMWBuilder就属于具体建造者。

  • Director 导演

负责安排已有模块的顺序,然后告诉Builder开始建造,在上面的例子中就是我们的老大,牛叉公司找到老大,说我要这个,这个,那个类型的车辆模型,然后老大就把命令传递给我,我和我的团队就开始拼命的建造,于是一个项目建设完毕了。

建造者模式的通用源代码也比较简单,先看Product类,通常它是一个组合或继承(如模板方法模式)产生的类,如代码清单11-12所示。

代码清单11-12 产品类

1
2
3
4
5
6
7
8
9
public class Product {
 
public void doSomething(){
 
//独立业务处理
 
}
 
}

抽象建造者如代码清单11-13所示。

代码清单11-13 抽象建造者

1
2
3
4
5
6
7
8
9
10
11
public abstract class Builder {
 
//设置产品的不同部分,以获得不同的产品
 
public abstract void setPart();
 
//建造产品
 
public abstract Product buildProduct();
 
}

其中,setPart方法是零件的配置,什么是零件?其他的对象,获得一个不同零件,或者不同的装配顺序就可能产生不同的产品。具体的建造者如代码清单11-14所示。

代码清单11-14 具体建造者

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
public class ConcreteProduct extends Builder {
 
private Product product = new Product();
 
//设置产品零件
 
public void setPart(){
 
/*
 
* 产品类内的逻辑处理
 
*/
 
}
 
//组建一个产品
 
public Product buildProduct() {
 
return product;
 
}
 
}

需要注意的是,如果有多个产品类就有几个具体的建造者,而且这多个产品类具有相同接口或抽象类,参考我们上面的例子。

导演类如代码清单11-15所示。

代码清单11-15 导演类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Director {
 
private Builder builder = new ConcreteProduct();
 
//构建不同的产品
 
public Product getAProduct(){
 
builder.setPart();
 
/*
 
* 设置不同的零件,产生不同的产品
 
*/
 
return builder.buildProduct();
 
}
 
}

导演类就是起到封装的作用,避免高层模块深入到建造者内部的实现类。当然,在建造者模式比较庞大时,导演类可以有多个。

11.3 建造者模式的应用

1. 建造者模式的优点

  • 封装性

使用建造者模式可以使客户端不必知道产品内部组成的细节,如例子中我们就不需要关心每一个具体的模型内部是如何实现的,产生的对象类型就是CarModel。

  • 建造者独立,容易扩展

BenzBuilder和BMWBuilder是相互独立的,对系统的扩展非常有利。

  • 便于控制细节风险

由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

2. 建造者模式的使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式是非常合适。
  • 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。该种场 景,只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反设计最初目标。

3. 建造者模式的注意事项

建造者模式关注的是的零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同。

11.4 建造者模式的扩展

已经不用扩展了,因为我们在汽车模型制造的例子中已经对建造者模式进行了扩展,引入了模板方法模式,可能大家会比较疑惑,为什么在其他介绍设计模式的书籍 上创建者模式并不是这样说的,读者请注意,建造者模式中还有一个角色没有说明,就是零件,建造者怎么去建造一个对象?是零件的组装,组装顺序不同对象效能 也不同,这才是建造者模式要表达的核心意义,而怎么才能更好的达到这种效果呢?引入模板方法模式是一个非常简单而有效的办法。

大家看到这里估计就开始犯嘀咕了,这个建造者模式和工厂模式非常相似呀,Yes,是的,是非常相似,但是记住一点你就可以游刃有余的使用了:建造者模式最 主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,通俗的说就是零件的装配,顺序不同产生的对象也不同;而工厂方法则重点是创建,创建零 件时它的主要职责,你要什么对象我创造一个对象出来,组装顺序则不是他关心的。

11.5 最佳实践

再次说明,在使用建造者模式的时候考虑一下模板方法模式,别孤立的思考一个模式,僵化的套用一个模式会让受害无穷!

如果你已经看懂本章节举的例子,并认可这种建造者模式,那你就放心使用,比单独使用某些书上的纯建造者是高效、简洁得多。

标签: 设计模式

转:builder模式分析的更多相关文章

  1. Net设计模式实例之建造者模式(Builder Pattern)

    一.建造者模式简介(Brief Introduction) 建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使的同样的构建过程可以创建不同的表示. 建造者模式的优点是 ...

  2. C#设计模式——建造者模式(Builder Pattern)

    1.建造者模式简介 1.1>.定义 建造者模式(Builder)将复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示. 1.2>.使用频率  中低 1.3>.原型模式应用 ...

  3. 23种设计模式--建造者模式-Builder Pattern

    一.建造模式的介绍       建造者模式就是将零件组装成一个整体,用官方一点的话来讲就是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.生活中比如说组装电脑,汽车等等这些都是建 ...

  4. PHP设计模式(五)建造者模式(Builder For PHP)

    建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示的设计模式. 设计场景: 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,爱好等信息,才能获得用 ...

  5. 每天一个设计模式-7 生成器模式(Builder)

    每天一个设计模式-7 生成器模式(Builder) 一.实际问题 在讨论工厂方法模式的时候,提到了一个导出数据的应用框架,但是并没有涉及到导出数据的具体实现,这次通过生成器模式来简单实现导出成文本,X ...

  6. Stack Overflow 排错翻译 - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder

    Stack Overflow 排错翻译  - Closing AlertDialog.Builder in Android -Android环境中关闭AlertDialog.Builder 转自:ht ...

  7. Joshua Bloch错了? ——适当改变你的Builder模式实现

    注:这一系列都是小品文.它们偏重的并不是如何实现模式,而是一系列在模式实现,使用等众多方面绝对值得思考的问题.如果您仅仅希望知道一个模式该如何实现,那么整个系列都会让您失望.如果您希望更深入地了解各个 ...

  8. c++ builder 2010 错误 F1004 Internal compiler error at 0x9740d99 with base 0x9

    今天遇到一个奇怪的问题,拷贝项目后,在修改,会出现F1004 Internal compiler error at 0x9740d99 with base 0x9 ,不管怎么改,删除改动,都没用,关闭 ...

  9. c++ builder TIdHttp 获取不到cookie

    用c++ builder 的TIdHttp组件Get一个ASP.Net MVC服务器的一个页面,获取页面中Cookie信息,修改后Post到服务器上去. 在本地调试的时候可以获取到,部署到服务器上就获 ...

随机推荐

  1. 【拥抱元宇宙】创建你的第一个Unity程序HelloWorld,并发布

    第一个Unity程序--Hello World. 1.需要先下载一个Unity Hub,以及安装Unity编辑器.Unity Hub需要登陆,激活码可以选择个人用户,免费的.免费的无法改变启动画面,其 ...

  2. 会话Cookie与session的关系

    在通常的使用中,我们只知道session信息是存放在服务器端,而cookie是存放在客户端.但服务器如何使用session和客户端之间进行通信,以及jsessionId是怎么回事,这并没有一个完整和正 ...

  3. [bzoj5510]唱跳rap和篮球

    显然答案可以理解为有(不是仅有)0对情况-1对情况+2对情况-- 考虑这个怎么计算,先计算这t对情况的位置,有c(n-3t,t)种情况(可以理解为将这4个点缩为1个,然后再从中选t个位置),然后相当于 ...

  4. [hdu6581]Vacation

    首先发现,最终第0辆车一定被堵在某一辆车前,那么等价于它的初始位置就在(那辆车的位置+中间车的车长)/那辆车的速度,其中最大的那个就是答案因此得出结论:$ans=max((\sum_{j=1}^{i} ...

  5. ICCV 2021口罩人物身份鉴别全球挑战赛冠军方案分享

    1. 引言 10月11-17日,万众期待的国际计算机视觉大会 ICCV 2021 (International Conference on Computer Vision) 在线上如期举行,受到全球计 ...

  6. 面向对象中static的理解(1)

    class 对象名字{ data members; static data members; function members; static function members; } 每创建一个对象, ...

  7. “微信小程序从分享卡片进入,第一次获取不到用户uid、第二次能获取到用户uid”解决方法

    用uniapp开发微信小程序时,有一个需求是分享罐表详情页面给其它用户,其它用户(在已经登录的状态下)点击分享卡片可以直接跳转到该罐表详情页,且能显示自己是否已经收藏该罐表(收藏状态由用户uid和罐表 ...

  8. Topcoder 15405 - PrettyLiar(可删除背包+前缀和优化 dp)

    题面传送门 题意: 给出两个长度为 \(n\) 的数组 \(a,b\) 和一个整数 \(s\). 你可以任意重排数组 \(a,b\),总共 \((n!)^2\) 种方案. 现在又两个人 A,B 来玩游 ...

  9. Generic recipe for data analysis with general linear model

    Generic recipe for data analysis with general linear model Courtesy of David Schneider State populat ...

  10. perl中tr的用法(转载)

    转载:http://blog.sina.com.cn/s/blog_4a0824490101hncz.html (1)/c表示把匹配不上的字符进行替换. $temp="AAAABCDEF&q ...