设计模式 - 吕震宇
.NET设计模式系列文章
薛敬明的专栏
乐在其中设计模式(C#)
http://www.cnblogs.com/zhenyulu/articles/37378.html
一、 建造者(Builder)模式
建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
对象性质的建造
有些情况下,一个对象会有一些重要的性质,在它们没有恰当的值之前,对象不能作为一个完整的产品使用。比如,一个电子邮件有发件人地址、收件人地址、主题、内容、附录等部分,而在最起码的收件人地址未被赋值之前,这个电子邮件不能发出。
有些情况下,一个对象的一些性质必须按照某个顺序赋值才有意义。在某个性质没有赋值之前,另一个性质则无法赋值。这些情况使得性质本身的建造涉及到复杂的商业逻辑。
这时候,此对象相当于一个有待建造的产品,而对象的这些性质相当于产品的零件,建造产品的过程就是组合零件的过程。由于组合零件的过程很复杂,因此,这些"零件"的组合过程往往被"外部化"到一个称作建造者的对象里,建造者返还给客户端的是一个全部零件都建造完毕的产品对象。
命名的考虑
之所以使用"建造者"而没有用"生成器"就是因为用零件生产产品,"建造"更为合适,"创建"或"生成"不太恰当。
二、 Builder模式的结构:

建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者(ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的方法:一个是建造方法,另一个是结果返还方法。
具体建造者(Concrete Builder)角色:担任这个角色的是于应用程序紧密相关的类,它们在应用程序调用下创建产品实例。这个角色主要完成的任务包括:
- 实现Builder角色提供的接口,一步一步完成创建产品实例的过程。
- 在建造过程完成后,提供产品的实例。
指导者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。导演者并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者对象。
产品(Product)角色:产品便是建造中的复杂对象。
指导者角色是于客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但却不为客户端所知。
三、 程序举例:
该程序演示了Builder模式一步一步完成构件复杂产品的过程。用户可以控制生成过程以及生成不同对象。
//
Builder pattern -- Structural example
using
System;
using
System.Collections;

//
"Director"
class
Director
{
// Methods
public void Construct( Builder builder )
{
builder.BuildPartA();
builder.BuildPartB();
}
}

//
"Builder"
abstract
class
Builder
{
// Methods
abstract public void BuildPartA();
abstract public void BuildPartB();
abstract public Product GetResult();
}

//
"ConcreteBuilder1"
class
ConcreteBuilder1 : Builder
{
// Fields
private Product product;

// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartA" );
}

override public void BuildPartB()
{
product.Add( "PartB" );
}

override public Product GetResult()
{
return product;
}
}

//
"ConcreteBuilder2"
class
ConcreteBuilder2 : Builder
{
// Fields
private Product product;

// Methods
override public void BuildPartA()
{
product = new Product();
product.Add( "PartX" );
}

override public void BuildPartB()
{
product.Add( "PartY" );
}

override public Product GetResult()
{
return product;
}
}

//
"Product"
class
Product
{
// Fields
ArrayList parts = new ArrayList();
// Methods
public void Add( string part )
{
parts.Add( part );
}

public void Show()
{
Console.WriteLine( " Product Parts -------" );
foreach( string part in parts )
Console.WriteLine( part );
}
}

/// <summary>
/// Client test
/// </summary>
public
class
Client
{
public static void Main( string[] args )
{
// Create director and builders
Director director = new Director( );

Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();

// Construct two products
director.Construct( b1 );
Product p1 = b1.GetResult();
p1.Show();

director.Construct( b2 );
Product p2 = b2.GetResult();
p2.Show();
}
}
四、 建造者模式的活动序列:

客户端负责创建指导者和具体建造者对象。然后,客户把具体建造者对象交给指导者。客户一声令下,指导者操纵建造者开始创建产品。当产品创建完成后,建造者把产品返还给客户端。
五、 建造者模式的实现:
下面的程序代码演示了Shop对象使用VehicleBuilders来建造不同的交通工具。该例子使用了Builder模式顺序建造交通工具的不同部分。
//
Builder pattern -- Real World example
using
System;
using
System.Collections;

//
"Director"
class
Shop
{
// Methods
public void Construct( VehicleBuilder vehicleBuilder )
{
vehicleBuilder.BuildFrame();
vehicleBuilder.BuildEngine();
vehicleBuilder.BuildWheels();
vehicleBuilder.BuildDoors();
}
}

//
"Builder"
abstract
class
VehicleBuilder
{
// Fields
protected Vehicle vehicle;

// Properties
public Vehicle Vehicle
{
get{ return vehicle; }
}

// Methods
abstract public void BuildFrame();
abstract public void BuildEngine();
abstract public void BuildWheels();
abstract public void BuildDoors();
}

//
"ConcreteBuilder1"
class
MotorCycleBuilder : VehicleBuilder
{
// Methods
override public void BuildFrame()
{
vehicle = new Vehicle( "MotorCycle" );
vehicle[ "frame" ] = "MotorCycle Frame";
}

override public void BuildEngine()
{
vehicle[ "engine" ] = "500 cc";
}

override public void BuildWheels()
{
vehicle[ "wheels" ] = "2";
}

override public void BuildDoors()
{
vehicle[ "doors" ] = "0";
}
}

//
"ConcreteBuilder2"
class
CarBuilder : VehicleBuilder
{
// Methods
override public void BuildFrame()
{
vehicle = new Vehicle( "Car" );
vehicle[ "frame" ] = "Car Frame";
}

override public void BuildEngine()
{
vehicle[ "engine" ] = "2500 cc";
}

override public void BuildWheels()
{
vehicle[ "wheels" ] = "4";
}

override public void BuildDoors()
{
vehicle[ "doors" ] = "4";
}
}

//
"ConcreteBuilder3"
class
ScooterBuilder : VehicleBuilder
{
// Methods
override public void BuildFrame()
{
vehicle = new Vehicle( "Scooter" );
vehicle[ "frame" ] = "Scooter Frame";
}

override public void BuildEngine()
{
vehicle[ "engine" ] = "none";
}

override public void BuildWheels()
{
vehicle[ "wheels" ] = "2";
}

override public void BuildDoors()
{
vehicle[ "doors" ] = "0";
}
}

//
"Product"
class
Vehicle
{
// Fields
private string type;
private Hashtable parts = new Hashtable();

// Constructors
public Vehicle( string type )
{
this.type = type;
}

// Indexers
public object this[ string key ]
{
get{ return parts[ key ]; }
set{ parts[ key ] = value; }
}

// Methods
public void Show()
{
Console.WriteLine( " ---------------------------");
Console.WriteLine( "Vehicle Type: "+ type );
Console.WriteLine( " Frame : " + parts[ "frame" ] );
Console.WriteLine( " Engine : "+ parts[ "engine"] );
Console.WriteLine( " #Wheels: "+ parts[ "wheels"] );
Console.WriteLine( " #Doors : "+ parts[ "doors" ] );
}
}

/// <summary>
/// BuilderApp test
/// </summary>
public
class
BuilderApp
{
public static void Main( string[] args )
{
// Create shop and vehicle builders
Shop shop = new Shop();
VehicleBuilder b1 = new ScooterBuilder();
VehicleBuilder b2 = new CarBuilder();
VehicleBuilder b3 = new MotorCycleBuilder();

// Construct and display vehicles
shop.Construct( b1 );
b1.Vehicle.Show();

shop.Construct( b2 );
b2.Vehicle.Show();

shop.Construct( b3 );
b3.Vehicle.Show();
}
}
六、 建造者模式的演化
建造者模式在使用的过程中可以演化出多种形式。
省略抽象建造者角色
如果系统中只需要一个具体建造者的话,可以省略掉抽象建造者。这时代码可能如下:
//
"Director"
class
Director
{
private ConcreteBuilder builder;

// Methods
public void Construct()
{
builder.BuildPartA();
builder.BuildPartB();
}
}
省略指导者角色
在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略掉指导者角色。让Builder角色自己扮演指导者与建造者双重角色。这时代码可能如下:
public
class
Builder
{
private Product product = new Product();

public void BuildPartA()
{
//Some code here
}

public void BuildPartB()
{
//Some code here
}

public Product GetResult()
{
return product;
}

public void Construct()
{
BuildPartA();
BuildPartB();
}
}
同时,客户端也需要进行相应的调整,如下:
public
class
Client
{
private static Builder builder;

public static void Main()
{
builder = new Builder();
builder.Construct();
Product product = builder.GetResult();
}
}
C#中的StringBuilder就是这样一个例子。
七、 在什么情况下使用建造者模式
以下情况应当使用建造者模式:
1、 需要生成的产品对象有复杂的内部结构。
2、 需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
使用建造者模式主要有以下效果:
1、 建造模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
2、 每一个Builder都相对独立,而与其它的Builder无关。
3、 模式所建造的最终产品更易于控制。
参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
实际上,有些时候两者是结合使用的。我见过BuilderFactory。在Java的DOM模型中就使用了BuilderFactory。
Java 环境中,解析文件是一个三步过程:
1、创建 DocumentBuilderFactory。 DocumentBuilderFactory 对象创建 DocumentBuilder。
2、创建 DocumentBuilder。 DocumentBuilder 执行实际的解析以创建 Document 对象。
3、解析文件以创建 Document 对象。
关键代码如下:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import org.w3c.dom.Document;
public class OrderProcessor {
public static void main (String args[]) {
File docFile = new File("orders.xml");
Document doc = null;
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);
} catch (Exception e) {
System.out.print("Problem parsing the file: "+e.getMessage());
}
}
}
我们可以给工厂“下订单”,决定生产什么样子的Builder:
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(docFile);
} catch (Exception e) {
其中dbf.setValidating(true);命令就是控制工厂的生产方式,这样建造出的DocumentBuilder在解析过程中就要验证XML文档的正确性。
很有创意的!
对!此时相当于builder是生产线,用于组装产品,而工厂则决定了生产线的类型。
在GOF的23种模式中,很多都可以结合的。比如singleTon和工厂模式。
建造者模式(Builder Pattern)
——.NET设计模式系列之四
Terrylee,2005年12月17日
概述
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式。
本文通过现实生活中的买KFC的例子,用图解的方式来诠释建造者模式。
意图
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
模型图

生活中的例子
生成器模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。这种模式用于快餐店制作儿童餐。典型的儿童餐包括一个主食,一个辅食,一杯饮料和一个玩具(例如汉堡、炸鸡、可乐和玩具车)。这些在不同的儿童餐中可以是不同的,但是组合成儿童餐的过程是相同的。无论顾客点的是汉堡,三名治还是鸡肉,过程都是一样的。柜台的员工直接把主食,辅食和玩具放在一起。这些是放在一个袋子中的。饮料被倒入杯中,放在袋子外边。这些过程在相互竞争的餐馆中是同样的。

实现过程图解
在这里我们还是以去KFC店买套餐为例子,示意图如下:

客户端:顾客。想去买一套套餐(这里面包括汉堡,可乐,薯条),可以有1号和2号两种套餐供顾客选择。
指导者角色:收银员。知道顾客想要买什么样的套餐,并告诉餐馆员工去准备套餐。
建造者角色:餐馆员工。按照收银员的要求去准备具体的套餐,分别放入汉堡,可乐,薯条等。
产品角色:最后的套餐,所有的东西放在同一个盘子里面。
下面开始我们的买套餐过程。
1.客户创建Derector对象,并用它所想要的Builder对象进行配置。顾客进入KFC店要买套餐,先找到一个收银员,相当于创建了一个指导者对象。这位收银员给出两种套餐供顾客选择:1普通套餐,2黄金套餐。完成的工作如时序图中红色部分所示。

程序实现:
1
using
System;
2
using
System.Configuration;
3
using
System.Reflection;
4

5
namespace
KFC
6
{
7
/// <summary>
8
/// Client 类
9
/// </summary>
10
public class Client
11
{
12
public static void Main(string[] args)
13
{
14
FoodManager foodmanager = new FoodManager();
15
16
Builder instance;
17
18
Console.WriteLine("Please Enter Food No:");
19
20
string No = Console.ReadLine();
21
22
string foodType = ConfigurationSettings.AppSettings["No"+No];
23
24
instance = (Builder)Assembly.Load("KFC").CreateInstance("KFC." + foodType);
25
26
foodmanager.Construct(instance);
27
}
28
}
29
}
30
产品(套餐)类:
1
using
System;
2
using
System.Collections;
3

4
namespace
KFC
5
{
6
/// <summary>
7
/// Food类,即产品类
8
/// </summary>
9
public class Food
10
{
11
Hashtable food = new Hashtable();
12
13
/// <summary>
14
/// 添加食品
15
/// </summary>
16
/// <param name="strName">食品名称</param>
17
/// <param name="Price">价格</param>
18
public void Add(string strName,string Price)
19
{
20
food.Add(strName,Price);
21
}
22
23
/// <summary>
24
/// 显示食品清单
25
/// </summary>
26
public void Show()
27
{
28
IDictionaryEnumerator myEnumerator = food.GetEnumerator();
29
Console.WriteLine("Food List:");
30
Console.WriteLine("------------------------------");
31
string strfoodlist = "";
32
while(myEnumerator.MoveNext())
33
{
34
strfoodlist = strfoodlist + "\n\n" + myEnumerator.Key.ToString();
35
strfoodlist = strfoodlist + ":\t" +myEnumerator.Value.ToString();
36
}
37
Console.WriteLine(strfoodlist);
38
Console.WriteLine("\n------------------------------");
39
}
40
}
41
}
42
2.指导者通知建造器。收银员(指导者)告知餐馆员工准备套餐。这里我们准备套餐的顺序是:放入汉堡,可乐倒入杯中,薯条放入盒中,并把这些东西都放在盘子上。这个过程对于普通套餐和黄金套餐来说都是一样的,不同的是它们的汉堡,可乐,薯条价格不同而已。如时序图红色部分所示:

程序实现:
1
using
System;
2

3
namespace
KFC
4
{
5
/// <summary>
6
/// FoodManager类,即指导者
7
/// </summary>
8
public class FoodManager
9
{
10
public void Construct(Builder builder)
11
{
12
builder.BuildHamb();
13
14
builder.BuildCoke();
15
16
builder.BuildChip();
17
}
18
}
19
}
20
3.建造者处理指导者的要求,并将部件添加到产品中。餐馆员工(建造者)按照收银员要求的把对应的汉堡,可乐,薯条放入盘子中。这部分是建造者模式里面富于变化的部分,因为顾客选择的套餐不同,套餐的组装过程也不同,这步完成产品对象的创建工作。
程序实现:
1
using
System;
2

3
namespace
KFC
4
{
5
/// <summary>
6
/// Builder类,即抽象建造者类,构造套餐
7
/// </summary>
8
public abstract class Builder
9
{
10
/// <summary>
11
/// 添加汉堡
12
/// </summary>
13
public abstract void BuildHamb();
14
15
/// <summary>
16
/// 添加可乐
17
/// </summary>
18
public abstract void BuildCoke();
19
20
/// <summary>
21
/// 添加薯条
22
/// </summary>
23
public abstract void BuildChip();
24
25
/// <summary>
26
/// 返回结果
27
/// </summary>
28
/// <returns>食品对象</returns>
29
public abstract Food GetFood();
30
}
31
}
32
1
using
System;
2

3
namespace
KFC
4
{
5
/// <summary>
6
/// NormalBuilder类,具体构造者,普通套餐
7
/// </summary>
8
public class NormalBuilder:Builder
9
{
10
private Food NormalFood = new Food();
11
12
public override void BuildHamb()
13
{
14
NormalFood.Add("NormalHamb","¥10.50");
15
}
16
17
public override void BuildCoke()
18
{
19
NormalFood.Add("CokeCole","¥4.50");
20
}
21
22
public override void BuildChip()
23
{
24
NormalFood.Add("FireChips","¥2.00");
25
}
26
27
public override Food GetFood()
28
{
29
return NormalFood;
30
}
31
32
}
33
}
34
1
using
System;
2

3
namespace
KFC
4
{
5
/// <summary>
6
/// GoldBuilder类,具体构造者,黄金套餐
7
/// </summary>
8
public class GoldBuilder:Builder
9
{
10
private Food GoldFood = new Food();
11
12
public override void BuildHamb()
13
{
14
GoldFood.Add("GoldHamb","¥13.50");
15
}
16
17
public override void BuildCoke()
18
{
19
GoldFood.Add("CokeCole","¥4.50");
20
}
21
22
public override void BuildChip()
23
{
24
GoldFood.Add("FireChips","¥3.50");
25
}
26
27
public override Food GetFood()
28
{
29
return GoldFood;
30
}
31
32
}
33
}
34
4.客户从建造者检索产品。从餐馆员工准备好套餐后,顾客再从餐馆员工那儿拿回套餐。这步客户程序要做的仅仅是取回已经生成的产品对象,如时序图中红色部分所示。

完整的客户程序:
1
using
System;
2
using
System.Configuration;
3
using
System.Reflection;
4

5
namespace
KFC
6
{
7
/// <summary>
8
/// Client 类
9
/// </summary>
10
public class Client
11
{
12
public static void Main(string[] args)
13
{
14
FoodManager foodmanager = new FoodManager();
15
16
Builder instance;
17
18
Console.WriteLine("Please Enter Food No:");
19
20
string No = Console.ReadLine();
21
22
string foodType = ConfigurationSettings.AppSettings["No"+No];
23
24
instance = (Builder)Assembly.Load("KFC").CreateInstance("KFC." + foodType);
25
26
foodmanager.Construct(instance);
27
28
Food food = instance.GetFood();
29
food.Show();
30
31
Console.ReadLine();
32
}
33
}
34
}
35
通过分析不难看出,在这个例子中,在准备套餐的过程是稳定的,即按照一定的步骤去做,而套餐的组成部分则是变化的,有可能是普通套餐或黄金套餐等。这个变化就是建造者模式中的“变化点“,就是我们要封装的部分。
另外一个例子
在这里我们再给出另外一个关于建造房子的例子。客户程序通过调用指导者 (CDirector class)的BuildHouse()方法来创建一个房子。该方法有一个布尔型的参数blnBackyard,当blnBackyard为假时指导者将创建一个Apartment(Concrete Builder),当它为真时将创建一个Single Family Home(Concrete Builder)。这两种房子都实现了接口Ihouse。
程序实现:
1
//
关于建造房屋的例子
2
using
System;
3
using
System.Collections;
4

5
/// <summary>
6
/// 抽象建造者
7
/// </summary>
8
public
interface
IHouse
9
{
10
bool GetBackyard();
11
long NoOfRooms();
12
string Description();
13
}
14

15
/// <summary>
16
/// 具体建造者
17
/// </summary>
18
public
class
CApt:IHouse
19
{
20
private bool mblnBackyard;
21
private Hashtable Rooms;
22
public CApt()
23
{
24
CRoom room;
25
Rooms = new Hashtable();
26
room = new CRoom();
27
room.RoomName = "Master Bedroom";
28
Rooms.Add ("room1",room);
29
30
room = new CRoom();
31
room.RoomName = "Second Bedroom";
32
Rooms.Add ("room2",room);
33
34
room = new CRoom();
35
room.RoomName = "Living Room";
36
Rooms.Add ("room3",room);
37
38
mblnBackyard = false;
39
}
40
41
public bool GetBackyard()
42
{
43
return mblnBackyard;
44
}
45
public long NoOfRooms()
46
{
47
return Rooms.Count;
48
}
49
public string Description()
50
{
51
IDictionaryEnumerator myEnumerator = Rooms.GetEnumerator();
52
string strDescription;
53
strDescription = "This is an Apartment with " + Rooms.Count + " Rooms \n";
54
strDescription = strDescription + "This Apartment doesn't have a backyard \n";
55
while (myEnumerator.MoveNext())
56
{
57
strDescription = strDescription + "\n" + myEnumerator.Key + "\t" + ((CRoom)myEnumerator.Value).RoomName;
58
}
59
return strDescription;
60
}
61
}
62

63
/// <summary>
64
/// 具体建造者
65
/// </summary>
66
public
class
CSFH:IHouse
67
{
68
private bool mblnBackyard;
69
private Hashtable Rooms;
70
public CSFH()
71
{
72
CRoom room;
73
Rooms = new Hashtable();
74
75
room = new CRoom();
76
room.RoomName = "Master Bedroom";
77
Rooms.Add ("room1",room);
78
79
room = new CRoom();
80
room.RoomName = "Second Bedroom";
81
Rooms.Add ("room2",room);
82
83
room = new CRoom();
84
room.RoomName = "Third Room";
85
Rooms.Add ("room3",room);
86
87
room = new CRoom();
88
room.RoomName = "Living Room";
89
Rooms.Add ("room4",room);
90
91
room = new CRoom();
92
room.RoomName = "Guest Room";
93
Rooms.Add ("room5",room);
94
95
mblnBackyard = true;
96
97
}
98
99
public bool GetBackyard()
100
{
101
return mblnBackyard;
102
}
103
public long NoOfRooms()
104
{
105
return Rooms.Count;
106
}
107
public string Description()
108
{
109
IDictionaryEnumerator myEnumerator = Rooms.GetEnumerator();
110
string strDescription;
111
strDescription = "This is an Single Family Home with " + Rooms.Count + " Rooms \n";
112
strDescription = strDescription + "This house has a backyard \n";
113
while (myEnumerator.MoveNext())
114
{
115
strDescription = strDescription + "\n" + myEnumerator.Key + "\t" + ((CRoom)myEnumerator.Value).RoomName;
116
}
117
return strDescription;
118
}
119
}
120

121
public
interface
IRoom
122
{
123
string RoomName{get;set;}
124
}
125

126
public
class
CRoom:IRoom
127
{
128
private string mstrRoomName;
129
public string RoomName
130
{
131
get
132
{
133
return mstrRoomName;
134
}
135
set
136
{
137
mstrRoomName = value;
138
}
139
}
140
}
141

142
/// <summary>
143
/// 指导者
144
/// </summary>
145
public
class
CDirector
146
{
147
public IHouse BuildHouse(bool blnBackyard)
148
{
149
if (blnBackyard)
150
{
151
return new CSFH();
152
}
153
else
154
{
155
return new CApt();
156
}
157
}
158
}
159

160
/// <summary>
161
/// 客户程序
162
/// </summary>
163
public
class
Client
164
{
165
static void Main(string[] args)
166
{
167
CDirector objDirector = new CDirector();
168
IHouse objHouse;
169
170
string Input = Console.ReadLine();
171
objHouse = objDirector.BuildHouse(bool.Parse(Input));
172
173
Console.WriteLine(objHouse.Description());
174
Console.ReadLine();
175
}
176
}
177

178
建造者模式的几种演化
省略抽象建造者角色
系统中只需要一个具体建造者,省略掉抽象建造者,结构图如下:

指导者代码如下:
1
class
Director
2
{
3
private ConcreteBuilder builder;
4
5
public void Construct()
6
{
7
builder.BuildPartA();
8
builder.BuildPartB();
9
}
10
}
省略指导者角色
抽象建造者角色已经被省略掉,还可以省略掉指导者角色。让Builder角色自己扮演指导者与建造者双重角色。结构图如下:

建造者角色代码如下:
1
public
class
Builder
2
{
3
private Product product = new Product();
4
5
public void BuildPartA()
6
{
7
//

8
}
9
10
public void BuildPartB()
11
{
12
//

13
}
14
15
public Product GetResult()
16
{
17
return product;
18
}
19
20
public void Construct()
21
{
22
BuildPartA();
23
BuildPartB();
24
}
25
}
客户程序:
1
public
class
Client
2
{
3
private static Builder builder;
4
5
public static void Main()
6
{
7
builder = new Builder();
8
builder.Construct();
9
Product product = builder.GetResult();
10
}
11
}
合并建造者角色和产品角色
建造模式失去抽象建造者角色和指导者角色后,可以进一步退化,从而失去具体建造者角色,此时具体建造者角色和产品角色合并,从而使得产品自己就是自己的建造者。这样做混淆了对象的建造者和对象本身,但是有时候一个产品对象有着固定的几个零件,而且永远只有这几个零件,此时将产品类和建造类合并,可以使系统简单易读。结构图如下:

实现要点
1、建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
2、产品不需要抽象类,特别是由于创建对象的算法复杂而导致使用此模式的情况下或者此模式应用于产品的生成过程,其最终结果可能差异很大,不大可能提炼出一个抽象产品类。
3、创建者中的创建子部件的接口方法不是抽象方法而是空方法,不进行任何操作,具体的创建者只需要覆盖需要的方法就可以,但是这也不是绝对的,特别是类似文本转换这种情况下,缺省的方法将输入原封不动的输出是合理的缺省操作。
4、前面我们说过的抽象工厂模式(Abtract Factory)解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化,建造者模式常和组合模式(Composite Pattern)结合使用。
效果
1、建造者模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
2、每一个Builder都相对独立,而与其它的Builder无关。
3、可使对构造过程更加精细控制。
4、将构建代码和表示代码分开。
5、建造者模式的缺点在于难于应付“分步骤构建算法”的需求变动。
适用性
以下情况应当使用建造者模式:
1、需要生成的产品对象有复杂的内部结构。
2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
应用场景
1、 RTF文档交换格式阅读器。
2、 .NET环境下的字符串处理StringBuilder,这是一种简化了的建造者模式。
3、 ……
总结
建造者模式的实质是解耦组装过程和创建具体部件,使得我们不用去关心每个部件是如何组装的。
______________________________________________________________________________________
源程序下载:/Files/Terrylee/BuilderPattern.rar
参考资料:
《Java与设计模式》阎宏 著
《设计模式(中文版)》
《DesignPatternsExplained》
乐在其中设计模式(C#) - 建造者模式(Builder Pattern)
作者:
webabcd
介绍
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
示例
用同样的构建过程创建Sql和Xml的Insert()方法和Get()方法。
MessageModel
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// Message实体类
/// </summary>
public class MessageModel
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="msg">Message内容</param>
/// <param name="pt">Message发布时间</param>
public MessageModel(string msg, DateTime pt)
{
this._message = msg;
this._publishTime = pt;
}

private string _message;
/// <summary>
/// Message内容
/// </summary>
public string Message
{
get { return _message; }
set { _message = value; }
}

private DateTime _publishTime;
/// <summary>
/// Message发布时间
/// </summary>
public DateTime PublishTime
{
get { return _publishTime; }
set { _publishTime = value; }
}
}
}
SqlMessage
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// Sql方式操作Message
/// </summary>
public class SqlMessage
{
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public List<MessageModel> Get()
{
List<MessageModel> l = new List<MessageModel>();
l.Add(new MessageModel("SQL方式获取Message", DateTime.Now));

return l;
}

/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public bool Insert(MessageModel mm)
{
// 代码略
return true;
}
}
}
XmlMessage
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// Xml方式操作Message
/// </summary>
public class XmlMessage
{
/// <summary>
/// 获取Message
/// </summary>
/// <returns></returns>
public List<MessageModel> Get()
{
List<MessageModel> l = new List<MessageModel>();
l.Add(new MessageModel("XML方式获取Message", DateTime.Now));

return l;
}

/// <summary>
/// 插入Message
/// </summary>
/// <param name="mm">Message实体对象</param>
/// <returns></returns>
public bool Insert(MessageModel mm)
{
// 代码略
return true;
}
}
}
Operation
using
System;
using
System.Collections.Generic;
using
System.Text;

using
System.Reflection;

namespace
Pattern.Builder
{
/// <summary>
/// 操作(Product)
/// </summary>
public class Operation
{
private string _type;
private Dictionary<string, string> _dictionary;

/// <summary>
/// 构造函数
/// </summary>
/// <param name="type">产品类型</param>
public Operation(string type)
{
_dictionary = new Dictionary<string, string>();
this._type = type;
}

/// <summary>
/// 索引器
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string this[string key]
{
get { return _dictionary[key]; }
set { _dictionary[key] = value; }
}

/// <summary>
/// 获得结果
/// </summary>
/// <returns></returns>
public string GetResult()
{
Assembly assembly = Assembly.Load("Pattern.Builder");

MethodInfo methodGet = assembly.GetType("Pattern.Builder." + _dictionary["get"].Split('|')[0]).GetMethod(_dictionary["get"].Split('|')[1]);
object objGet = methodGet.Invoke(assembly.CreateInstance("Pattern.Builder." + _dictionary["get"].Split('|')[0]), null);
List<MessageModel> m = (List<MessageModel>)objGet;
MethodInfo methodInsert = assembly.GetType("Pattern.Builder." + _dictionary["insert"].Split('|')[0]).GetMethod(_dictionary["insert"].Split('|')[1]);
object objInsert = methodInsert.Invoke(assembly.CreateInstance("Pattern.Builder." + _dictionary["insert"].Split('|')[0]), new object[] { new MessageModel(_dictionary["insert"].Split('|')[2], Convert.ToDateTime(_dictionary["insert"].Split('|')[3])) });
bool b = (bool)objInsert;

return "类型为" + this._type + "的执行结果:<br />" + b.ToString() + "<br />" + m[0].Message + " " + m[0].PublishTime.ToString() + "<br />";
}
}
}
AbstractMessageBuilder
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// 抽象Message建造者(Builder)
/// </summary>
public abstract class AbstractMessageBuilder
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="type">产品类型</param>
public AbstractMessageBuilder(string type)
{
this._operation = new Operation(type);
}

/// <summary>
/// 操作(Product)
/// </summary>
protected Operation _operation;
/// <summary>
/// 操作(Product)
/// </summary>
public Operation Operation
{
get { return _operation; }
set { _operation = value; }
}

/// <summary>
/// 建造插入方法
/// </summary>
/// <param name="mm"></param>
abstract public void BuilderInsert(MessageModel mm);

/// <summary>
/// 建造获取方法
/// </summary>
abstract public void BuilderGet();
}
}
SqlMessageBuilder
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// SqlMessage建造者(ConcreteBuilder)
/// </summary>
public class SqlMessageBuilder : AbstractMessageBuilder
{
/// <summary>
/// 构造函数
/// </summary>
public SqlMessageBuilder()
: base("sql")
{
}

/// <summary>
/// 建造Sql插入方法
/// </summary>
/// <param name="mm">Message实体对象</param>
public override void BuilderInsert(MessageModel mm)
{
base._operation["insert"] = "SqlMessage|Insert|" + mm.Message + "|" + mm.PublishTime.ToString() + "";
}

/// <summary>
/// 建造Sql获取方法
/// </summary>
public override void BuilderGet()
{
base._operation["get"] = "SqlMessage|Get";
}
}
}
XmlMessageBuilder
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// XmlMessage建造者(ConcreteBuilder)
/// </summary>
public class XmlMessageBuilder : AbstractMessageBuilder
{
/// <summary>
/// 构造函数
/// </summary>
public XmlMessageBuilder()
: base("xml")
{
}

/// <summary>
/// 建造Xml插入方法
/// </summary>
/// <param name="mm">Message实体对象</param>
public override void BuilderInsert(MessageModel mm)
{
base._operation["insert"] = "XmlMessage|Insert|"+mm.Message+"|"+mm.PublishTime.ToString()+"";
}

/// <summary>
/// 建造Xml获取方法
/// </summary>
public override void BuilderGet()
{
base._operation["get"] = "XmlMessage|Get";
}
}
}
Message
using
System;
using
System.Collections.Generic;
using
System.Text;

namespace
Pattern.Builder
{
/// <summary>
/// 指导者(Director)
/// </summary>
public class Message
{
/// <summary>
/// 调用具体建造者角色以创建产品对象
/// </summary>
/// <param name="messageBuilder">AbstractMessageBuilder</param>
/// <param name="mm">Message实体对象</param>
public void Construct(AbstractMessageBuilder messageBuilder, MessageModel mm)
{
messageBuilder.BuilderInsert(mm);
messageBuilder.BuilderGet();
}
}
}
client
using
System;
using
System.Data;
using
System.Configuration;
using
System.Collections;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Web.UI.HtmlControls;

using
Pattern.Builder;

public
partial
class
Builder : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Message m = new Message();

AbstractMessageBuilder amb = new SqlMessageBuilder();
m.Construct(amb, new MessageModel("插入", DateTime.Now));

Response.Write(amb.Operation.GetResult());

amb = new XmlMessageBuilder();
m.Construct(amb, new MessageModel("插入", DateTime.Now));

Response.Write(amb.Operation.GetResult());
}
}
运行结果
类型为sql的执行结果:
True
SQL方式获取Message 2007-5-5 20:53:28
类型为xml的执行结果:
True
XML方式获取Message 2007-5-5 20:53:28
参考
http://www.dofactory.com/Patterns/PatternBuilder.aspx
OK
[源码下载]