建造者模式(Builder Pattern)
一、什么是建造者模式
意图:
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
模型图:
二、建造者模式实例
需求:这种模式用于快餐店制作儿童餐。典型的儿童餐包括一个主食,一个辅食,一杯饮料和一个玩具(例如汉堡、炸鸡、可乐和玩具车)。这些在不同的儿童餐中可以是不同的,但是组合成儿童餐的过程是相同的。无论顾客点的是汉堡,三名治还是鸡肉,过程都是一样的。柜台的员工直接把主食,辅食和玩具放在一起。这些是放在一个袋子中的。饮料被倒入杯中,放在袋子外边。这些过程在相互竞争的餐馆中是同样的。
示意图如下:
- 客户端:顾客。想去买一套套餐(这里面包括汉堡,可乐,薯条),可以有1号和2号两种套餐供顾客选择。
- 指导者角色:收银员。知道顾客想要买什么样的套餐,并告诉餐馆员工去准备套餐。
- 建造者角色:餐馆员工。按照收银员的要求去准备具体的套餐,分别放入汉堡,可乐,薯条等。
- 产品角色:最后的套餐,所有的东西放在同一个盘子里面。
1.客户创建Derector对象,并用它所想要的Builder对象进行配置。顾客进入KFC店要买套餐,先找到一个收银员,相当于创建了一个指导者对象。这位收银员给出两种套餐供顾客选择:1普通套餐,2黄金套餐。完成的工作如时序图中红色部分所示。
程序实现如下:
产品(套餐)类:
1 //产品套餐类 2 public class Food 3 { 4 Hashtable foodList = new Hashtable(); 5 6 //添加套餐中含有的食物 7 public void Add(string name, string price) 8 { 9 this.foodList.Add(name, price); 10 } 11 12 //显示套餐中的产品 13 public void Show() 14 { 15 IDictionaryEnumerator myEnumerator = foodList.GetEnumerator(); 16 Console.WriteLine("Food List"); 17 Console.WriteLine("=============================="); 18 19 string strfoodlist = ""; 20 while (myEnumerator.MoveNext()) 21 { 22 strfoodlist = strfoodlist + "\n" + myEnumerator.Key.ToString(); 23 strfoodlist = strfoodlist + ":\t" + myEnumerator.Value.ToString(); 24 } 25 26 Console.WriteLine(strfoodlist); 27 Console.WriteLine("\n------------------------------"); 28 } 29 }
指导者通知建造器。收银员(指导者)告知餐馆员工准备套餐。这里我们准备套餐的顺序是:放入汉堡,可乐倒入杯中,薯条放入盒中,并把这些东西都放在盘子上。这个过程对于普通套餐和黄金套餐来说都是一样的,不同的是它们的汉堡,可乐,薯条价格不同而已。如时序图红色部分所示:
程序实现:
1 //营业员(指导者角色) 2 public class FoodManager 3 { 4 public void Construct(FoodBuilder builder) 5 { 6 builder.BuildHumb(); 7 8 builder.BuildCoke(); 9 10 builder.BuildChips(); 11 } 12 }
建造者处理指导者的要求,并将部件添加到产品中。餐馆员工(建造者)按照收银员要求的把对应的汉堡,可乐,薯条放入盘子中。这部分是建造者模式里面富于变化的部分,因为顾客选择的套餐不同,套餐的组装过程也不同,这步完成产品对象的创建工作。
程序实现:
1 public abstract class FoodBuilder 2 { 3 public abstract void BuildHumb(); 4 5 public abstract void BuildCoke(); 6 7 public abstract void BuildChips(); 8 9 public abstract Food GetFood(); 10 }
1 //具体构造者(普通餐) 2 public class NormalFood : FoodBuilder 3 { 4 private Food normalFood = new Food(); 5 6 public override void BuildHumb() 7 { 8 normalFood.Add("NormalHumb", "$2.00"); 9 } 10 11 public override void BuildCoke() 12 { 13 normalFood.Add("SmallCoke", "$1.30"); 14 } 15 16 public override void BuildChips() 17 { 18 normalFood.Add("SmallChips", "$1.60"); 19 } 20 21 public override Food GetFood() 22 { 23 return normalFood; 24 } 25 } 26 27 //具体构造者(黄金套餐) 28 public class GoldenFood : FoodBuilder 29 { 30 private Food goldenFood = new Food(); 31 32 public override void BuildHumb() 33 { 34 goldenFood.Add("GoldenHumb", "$3.00"); 35 } 36 37 public override void BuildCoke() 38 { 39 goldenFood.Add("BigCoke", "$1.80"); 40 } 41 42 public override void BuildChips() 43 { 44 goldenFood.Add("BigChips", "$1.90"); 45 } 46 47 public override Food GetFood() 48 { 49 return goldenFood; 50 } 51 }
客户从建造者检索产品。从餐馆员工准备好套餐后,顾客再从餐馆员工那儿拿回套餐。这步客户程序要做的仅仅是取回已经生成的产品对象,如时序图中红色部分所示。
完整的客户程序:
1 public class Client 2 { 3 public static void Main(string[] args) 4 { 5 FoodManager manager = new FoodManager(); 6 FoodBuilder builder = new GoldenFood(); 7 8 manager.Construct(builder); 9 10 Food goldenFood = builder.GetFood(); 11 goldenFood.Show(); 12 13 Console.Read(); 14 } 15 16 }
通过分析不难看出,在这个例子中,在准备套餐的过程是稳定的,即按照一定的步骤去做,而套餐的组成部分则是变化的,有可能是普通套餐或黄金套餐等。这个变化就是建造者模式中的“变化点“,就是我们要封装的部分。
三、建造者模式的效果
- 建造者模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节
- 每一个Builder都相对独立,而与其它的Builder无关。
- 可使对构造过程更加精细控制。
- 将构建代码和表示代码分开。
- 建造者模式的缺点在于难于应付“分步骤构建算法”的需求变动。
四、建造者模式的适用性
- 需要生成的产品对象有复杂的内部结构。
- 需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
- 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。