艾伟_转载:.NET设计模式:建造者模式(Builder Pattern)

简介:   概述  在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。

  概述

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

  本文通过现实生活中的买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      /**/ ///  
 8      ///  Client 类
 9      ///  
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      /**/ ///  
 7      ///  Food类,即产品类
 8      ///  
 9      public   class  Food
10      {
11         Hashtable food  =   new  Hashtable();
12         
13          /**/ ///  
14          ///  添加食品
15          ///  
16          ///   食品名称
17          ///   价格
18          public   void  Add( string  strName, string  Price)
19          {
20             food.Add(strName,Price);
21         }
22         
23          /**/ ///  
24          ///  显示食品清单
25          ///  
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      /**/ ///  
 6      ///  FoodManager类,即指导者
 7      ///  
 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      /**/ ///  
 6      ///  Builder类,即抽象建造者类,构造套餐
 7      ///  
 8      public   abstract   class  Builder
 9      {    
10          /**/ ///  
11          ///  添加汉堡
12          ///  
13          public   abstract   void  BuildHamb();
14         
15          /**/ ///  
16          ///  添加可乐
17          ///  
18          public   abstract   void  BuildCoke();
19         
20          /**/ ///  
21          ///  添加薯条
22          ///  
23          public   abstract   void  BuildChip();
24         
25          /**/ ///  
26          ///  返回结果
27          ///  
28          ///   食品对象
29          public   abstract  Food GetFood();
30     }
31 }
32

 

 1 using  System;
 2
 3 namespace  KFC
 4 {
 5      /**/ ///  
 6      ///  NormalBuilder类,具体构造者,普通套餐
 7      ///  
 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      /**/ ///  
 6      ///  GoldBuilder类,具体构造者,黄金套餐
 7      ///  
 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      /**/ ///  
 8      ///  Client 类
 9      ///  
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 /**/ ///  
  6 ///  抽象建造者
  7 ///  
  8 public   interface  IHouse
  9 {
 10      bool  GetBackyard();
 11      long  NoOfRooms();
 12      string   Description();
 13 }
 14
 15 /**/ ///  
 16 ///  具体建造者
 17 ///  
 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 /**/ ///  
 64 ///  具体建造者
 65 ///  
 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 /**/ ///  
143 ///  指导者
144 ///  
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 /**/ ///  
161 ///  客户程序
162 ///  
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

目录
相关文章
|
22天前
|
设计模式 算法
建造者模式-大话设计模式
建造者模式-大话设计模式
11 1
|
16天前
|
设计模式 JavaScript
js设计模式【详解】—— 建造者模式
js设计模式【详解】—— 建造者模式
15 0
|
19天前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)
|
20天前
|
设计模式 算法 索引
程序技术好文:设计模式之美:Builder(生成器)
程序技术好文:设计模式之美:Builder(生成器)
|
21天前
|
设计模式 Java
Java设计模式:建造者模式之经典与流式的三种实现(四)
Java设计模式:建造者模式之经典与流式的三种实现(四)
|
21天前
|
设计模式 Java
Java设计模式之建造者模式详解
Java设计模式之建造者模式详解
|
21天前
|
设计模式 Java
设计模式之建造者模式
设计模式之建造者模式
|
27天前
|
设计模式
创建型设计模式之建造者模式
创建型设计模式之建造者模式
|
1月前
|
设计模式 算法 Java
【设计模式】建造者模式
【6月更文挑战第16天】建造者模式是创建型设计模式,用于分离对象构造的步骤和其表示,允许构造多种表示。适用于构建过程复杂且部分可变的情况。关键角色包括产品、抽象建造者、具体建造者和导演。例如,汉堡套餐的组合。优点是灵活性高、隐藏构建细节,缺点是可能增加代码冗余。与工厂模式对比,更关注构建顺序。示例中展示了Go语言的建造者模式实现,用于创建`ResourcePoolConfig`,通过Builder进行参数校验和构建。
16 0
|
8天前
|
设计模式 Go
Go语言设计模式:使用Option模式简化类的初始化
在Go语言中,面对构造函数参数过多导致的复杂性问题,可以采用Option模式。Option模式通过函数选项提供灵活的配置,增强了构造函数的可读性和可扩展性。以`Foo`为例,通过定义如`WithName`、`WithAge`、`WithDB`等设置器函数,调用者可以选择性地传递所需参数,避免了记忆参数顺序和类型。这种模式提升了代码的维护性和灵活性,特别是在处理多配置场景时。
41 8