`

转设计模式--第3章 建造者模式(Builder Pattern)

阅读更多
建造者模式(Builder Pattern)

——.NET设计模式系列之四

Terrylee,2005年12月17日

概述

在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式。

本文通过现实生活中的买KFC的例子,用图解的方式来诠释建造者模式。

意图

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

模型图

生活中的例子

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

实现过程图解

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

客户端:顾客。想去买一套套餐(这里面包括汉堡,可乐,薯条),可以有1号和2号两种套餐供顾客选择。

指导者角色:收银员。知道顾客想要买什么样的套餐,并告诉餐馆员工去准备套餐。

建造者角色:餐馆员工。按照收银员的要求去准备具体的套餐,分别放入汉堡,可乐,薯条等。

产品角色:最后的套餐,所有的东西放在同一个盘子里面。

下面开始我们的买套餐过程。

1.客户创建Derector对象,并用它所想要的Builder对象进行配置。顾客进入KFC店要买套餐,先找到一个收银员,相当于创建了一个指导者对象。这位收银员给出两种套餐供顾客选择:1普通套餐,2黄金套餐。完成的工作如时序图中红色部分所示。

程序实现:

1using System;
2using System.Configuration;
3using System.Reflection;
4
5namespace 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

产品(套餐)类:
1using System;
2using System.Collections;
3
4namespace 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.指导者通知建造器。收银员(指导者)告知餐馆员工准备套餐。这里我们准备套餐的顺序是:放入汉堡,可乐倒入杯中,薯条放入盒中,并把这些东西都放在盘子上。这个过程对于普通套餐和黄金套餐来说都是一样的,不同的是它们的汉堡,可乐,薯条价格不同而已。如时序图红色部分所示:

程序实现:

1using System;
2
3namespace 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.建造者处理指导者的要求,并将部件添加到产品中。餐馆员工(建造者)按照收银员要求的把对应的汉堡,可乐,薯条放入盘子中。这部分是建造者模式里面富于变化的部分,因为顾客选择的套餐不同,套餐的组装过程也不同,这步完成产品对象的创建工作。

程序实现:

1using System;
2
3namespace 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

1using System;
2
3namespace 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

1using System;
2
3namespace 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.客户从建造者检索产品。从餐馆员工准备好套餐后,顾客再从餐馆员工那儿拿回套餐。这步客户程序要做的仅仅是取回已经生成的产品对象,如时序图中红色部分所示。

完整的客户程序:

1using System;
2using System.Configuration;
3using System.Reflection;
4
5namespace 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//关于建造房屋的例子
  2using System;
  3using System.Collections;
  4
  5/// <summary>
  6/// 抽象建造者
  7/// </summary>
  8public interface IHouse
  9{
10    bool GetBackyard();
11    long NoOfRooms();
12    string  Description();
13}
14
15/// <summary>
16/// 具体建造者
17/// </summary>
18public 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>
66public 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
121public interface IRoom
122{
123    string RoomName{get;set;}
124}
125
126public 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>
145public 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>
163public 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》
分享到:
评论

相关推荐

    design-pattern-java.pdf

    对象的克隆——原型模式(四) 建造者模式-Builder Pattern 复杂对象的组装与创建——建造者模式(一) 复杂对象的组装与创建——建造者模式(二) 复杂对象的组装与创建——建造者模式(三) 七个结构型模式 七个...

    建造者模式(Builder Pattern)原理图

    建造者模式是一种创建型设计模式,用于简化复杂对象的创建过程。以下是建造者模式的关键概念和步骤: 1. **分离复杂对象的构建和表示**:建造者模式将一个复杂对象的构建过程与其最终的表示形式分离开来。这样做的...

    C#版 24种设计模式

    工厂方法模式(Factory Method Pattern) 观察者模式(Observer Pattern) 建造者模式(Builder Pattern) 解释器模式(Interpreter Pattern) 命令模式(Command Pattern) 模板方法模式(Template Method Pattern) 桥接模式...

    用Java实现23种设计模式

    建造者模式(Builder Pattern) 原型模式(Prototype Pattern) 2. 结构型模式 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite ...

    [创建型模式]设计模式之建造者模式(Builder Pattern)

    NULL 博文链接:https://jacky-dai.iteye.com/blog/2295399

    C#设计模式.PDF

    C#设计模式(8)-Builder Pattern 57 一、 建造者(Builder)模式 57 二、 Builder模式的结构: 58 三、 程序举例: 58 四、 建造者模式的活动序列: 62 五、 建造者模式的实现: 62 六、 建造者模式的演化 68 七、...

    Java24种设计模式,Java24种设计模式,24种设计模式,学会了这24种设计模式,可以打遍天下无敌手,设计模式非常重要

    10、建造者模式BUILDER PATTERN 11、桥梁模式BRIDGE PATTERN 12、命令模式COMMAND PATTERN 13、装饰模式DECORATOR PATTERN 14、迭代器模式ITERATOR PATTERN 15、组合模式COMPOSITE PATTERN 16、观察者模式...

    33种JAVA设计模式DEMO

    建造者模式(Builder Pattern) 原型模式(Prototype Pattern) 2 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 适配器模式(Adapter Pattern) 桥接...

    设计模式PPT

     建造者模式(Builder Pattern)  原型模式(Prototype Pattern)  单例模式(Singleton Pattern) 结构型模式用来处理类或者对象的组合,主要包含以下7种设计模式:  适配器模式(Adapter Pattern)  ...

    design-pattern-test:设计模式学习

    工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) 2 结构型模式---这些设计模式关注类和...

    C#设计模式大全

    C#设计模式(8)-Builder Pattern 一、 建造者(Builder)模式 二、 Builder模式的结构: 三、 程序举例: 四、 建造者模式的活动序列: 五、 建造者模式的实现: 六、 建造者模式的演化 七、 在什么情况下...

    C#设计模式_设计模式_C#_

    建造者模式(Builder) 4. 工厂方法模式(Factory Method) 5. 原型模式(Prototype)结构型: 6. 适配器模式(Adapter Pattern) 7. 桥接模式(Bridge Pattern) 8. 装饰模式(Decorator Pattern) 9. 组合模式(Composite ...

    单例模式源码java-DesignPattern:在个人自学阶段的23种设计模式代码的全部实现,全部使用Java编写,其中还包括各个设计模式在

    在个人自学阶段的23种设计模式代码的全部实现,全部使用Java编写,其中还包括各个设计模式在源码中的使用,每种设计模式都举了一个简单的小例子来进行实现,并加以注释 包名解释 一、DesignPattern 1.1 创建型模式 ...

    24种设计模式与6大设计原则

    建造者模式[BUILDER PATTERN] 策略模式 代理模式 单例模式 多例模式 工厂方法模式 抽象工厂模式 门面模式 适配器模式 模板方法模式 建造者模式 桥梁模式 命令模式 装饰模式 迭代器模式 组合模式 观察者模式 责任链...

    BuilderPattern.unitypackage

    BuilderPattern.unitypackage是一个建造者模式的例子。

    java设计模式源码-DesignPattern:设计模式(Java实现源码)

    建造者模式(builderPattern) 原型模式(prototypePattern) 适配器模式(adapterPattern) 桥接模式(bridgePattern) 过滤器模式(filterPattern) 组合模式(compositePattern) 装饰器模式(decoratorPattern) 外观模式...

    32种设计模式

    建造者模式(Builder) 4. 工厂方法模式(Factory Method) 5. 原型模式(Prototype) 结构型: 6. 适配器模式(Adapter Pattern) 7. 桥接模式(Bridge Pattern) 8. 装饰模式(Decorator ...

    Java设计模式,并加上个人理解

    9. 建造者模式 (Builder Pattern) 10. 原型模式 (Prototype Pattern) 11. 组合模式 (Composite Pattern) 12. 装饰者模式 (Decorator Pattern) 13. 访问者模式 (Visitor Pattern) 14. 迭代器模式 ...

    24个设计模式与6大设计原则

    第 10 章 建造者模式【BUILDER PATTERN】 82 第 11 章 桥梁模式【BRIDGE PATTERN】 97 第 12 章 命令模式【COMMAND PATTERN】 112 第 13 章 装饰模式【DECORATOR PATTERN】 126 第 14 章 迭代...

    Java高手真经 - Java Web系统设计与架构 源代码(一)设计模式

    pattern/src/creation/builder //11.4建造者模式 pattern/src/creation/prototype //11.5原型模式 pattern/src/structure/adapter //12.1适配器模式 pattern/src/structure/decorator //12.2装饰器模式 pattern/...

Global site tag (gtag.js) - Google Analytics