建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这是建造者模式的标准表达,不过看着让人迷惑,什么叫构建和表示的分离?一个对象使用构造函数构造之后不就固定了,只有通过它方法来改变它的属性吗?而且还要同样的构建过程搞出不同的表示,怎么可能呢?多写几个构造函数?
其实多写几个构造函数,根据不同参数设置对象不同的属性,也可以达到这样的效果,只是这样就非常麻烦了,每次要增加一种表示就要添加一个构造函数,将来构造函数会多得连自己都不记得了,这违背了开放-封闭的原则。
要不就只能设计几个set函数,每次属性不一样了,我就构造一个对象,然后用set函数改变对象的属性。这样也可以达到效果。只是代码就会非常冗余了,每个要用到这个对象的地方,都要写上好几句语句,一旦对象有点什么变化,还得到处都改一遍,这样就很容易出错,以后别人看着这种神逻辑和神代码估计也会崩溃了。而且这也违背了依赖倒转的原则。
于是大神们就开始想了,不能加很多构造函数,也不能直接用一堆set函数,然后发现,有些对象的构建是固定的几个步骤的,就像一条流水线一样,任何的产品都是通过每一个固定的步骤拼凑出来的。例如说一部手机,先放主板,再放屏幕,再放电池,再放外壳,贴个膜就能卖几千了,每次推出新产品,就换个更好的主板,换个大点的屏幕,再整个大容量电池,贴个超牛B的高透膜,又能卖出个新价钱。就是说,这些步骤都没有变,变的只是每个部分的东西。
这就是大神的厉害之处了,透过现象看本质,基本有变的,有不变的,那敢情好,面向对象的一个重要指导思想就是,封装隔离变化的,留出不变的。于是他们就用一个Builder类把步骤中的每个部分封装起来,这个类的主要作用就是生产每个部件,再抽象一下提升高度,这样就依赖倒转了,这样每次只需要添加一个类,这个类还是这几个部分,只是内部的实现已经不一样了,这样就满足了开放-封闭的原则了。但还是有一个问题,光有Builder类还不行,虽然产品的每个部分都有对应的函数,但是用起来的话,还是跟前面说的set函数一样,一用就要使用一大堆函数,也就是这变的东西是封装起来了,但这不变的东西还没留出来。这时,就添加一个Director类,这个类就是专门规定组装产品的步骤的,这样只要告诉Director使用哪个Builder,就能生产出不同的产品,对于客户端来说,只看到用了Director的一个construct函数,甚是方便。
再反过来看建造者模式的定义,构建指的就是生产一个产品的步骤,表示就是每个产品部分的具体实现,通过Director封装步骤,通过Builder封装产品部分的实现,再把他两隔离开,就能隔离变的,留出不变的供客户端使用。
图中可以看到,Product是必须要知道,没有抽象,但是这个产品却可以由不同的部分组合而成。Director里的construct也是固定,没有抽象出来,如果要更改步骤,也要添加一个函数,或者再添一个Diector,所以建造者模式一般应用于步骤不会发生大的变化,而产品会发生大变化的情况。
常用的场景
C#中的StringBuilder就是一个建造者的例子,但只是一个建造者,还缺一个Director,不能算一个完整的建造者模式。建造者模式一般应用于构建产品的步骤(也可以称为算法)不变,而每个步骤的具体实现又剧烈变化的情况。
优点
1.隔离了构建的步骤和具体的实现,为产品的具体实现提供了灵活度。
2.封装和抽象了每个步骤的实现,实现了依赖倒转原则。
3.封装了具体的步骤,减少了代码的冗余。
缺点
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
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
|
#include "stdafx.h" #include <stdlib.h> #include <iostream> using namespace std; //抽象类,用来安排创建人的具体流程,其他类必须遵循这个流程,但是可以自己具体实现 class CPersonBuilder { public : virtual void BuildHead()=0; virtual void BuildBody()=0; virtual void BuildArmLeft()=0; virtual void BuildArmRight()=0; virtual void BuildLegLeft()=0; virtual void BuildLegRight()=0; }; //创建瘦子的类 class CThinPersonBuilder: public CPersonBuilder { public : CThinPersonBuilder() { cout<< "is creating thin person " <<endl<<endl; } ~CThinPersonBuilder() { cout<< "is finished for thin person" <<endl<<endl; } public : void BuildHead() { cout<< "BuildHead" <<endl; } void BuildBody() { cout<< "BuildBody(thin)" <<endl; } void BuildArmLeft() { cout<< "BuildArmLeft" <<endl; } void BuildArmRight() { cout<< "BuildArmRight" <<endl; } void BuildLegLeft() { cout<< "BuildLegLeft" <<endl; } void BuildLegRight() { cout<< "BuildLegRight" <<endl; } }; //创建胖子的类 class CFatPersonBuilder: public CPersonBuilder { public : CFatPersonBuilder() { cout<< "is creating fat person" <<endl; } ~CFatPersonBuilder() { cout<< "is finished for fat person" <<endl; } public : void BuildHead() { cout<< "BuildHead" <<endl; } void BuildBody() { cout<< "BuildBody(Fat)" <<endl; } void BuildArmLeft() { cout<< "BuildArmLeft" <<endl; } void BuildArmRight() { cout<< "BuildArmRight" <<endl; } void BuildLegLeft() { cout<< "BuildLegLeft" <<endl; } void BuildLegRight() { cout<< "BuildLegRight" <<endl; } }; //指挥者类,用来指挥创建的人是瘦子还是胖子 class CPersonDirector { public : CPersonDirector(CPersonBuilder *p) { this ->m_p=p; } const void CreatePerson( void ) const { m_p->BuildHead(); m_p->BuildBody(); m_p->BuildArmLeft(); m_p->BuildArmRight(); m_p->BuildLegLeft(); m_p->BuildLegRight(); } private : CPersonBuilder *m_p; }; int _tmain( int argc, _TCHAR* argv[]) { cout<< "---------建造者模式测试案例------------------------" <<endl<<endl; CThinPersonBuilder *p_tp= new CThinPersonBuilder(); CPersonDirector *p_dtp= new CPersonDirector(p_tp); p_dtp->CreatePerson(); delete p_tp; delete p_dtp; p_tp=NULL; p_dtp=NULL; cout<<endl<<endl; CFatPersonBuilder *p_fp= new CFatPersonBuilder(); CPersonDirector *p_dfp= new CPersonDirector(p_fp); p_dfp->CreatePerson(); delete p_fp; delete p_dfp; p_fp=NULL; p_dfp=NULL; system ( "pause" ); return 0; } |