作者:
TerryLee
原文链接:
http://www.cnblogs.com/Terrylee/archive/2005/12/19/299878.html
建造者模式(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&nbs