设计模式(9):建造者模式

来源:互联网 发布:苹果怎么下载软件 编辑:程序博客网 时间:2024/05/18 02:36

建造者(Builder)模式又称为生成器模式,现在我们通过一个例子介绍它。

现在我们要程序画一个小人,要求小人有头、身体、双手、双腿。

可以创建一个窗体应用程序,首先建立一支黄色的画笔,在 pictureBox1 上画出小人即可:

   1: Pen p = new Pen(Color.Yellow);
   2:  
   3: Graphics gThin = pictureBox1.CreateGraphics();
   4:  
   5: gThin.DrawEllipse(p, 50, 20, 30, 30);//头
   6: gThin.DrawRectangle(p, 60, 50, 10, 50);//身体
   7: gThin.DrawLine(p, 60, 50, 40, 100);//左手
   8: gThin.DrawLine(p, 70, 50, 90, 100);//右手
   9: gThin.DrawLine(p, 60, 100, 45, 150);//左脚
  10: gThin.DrawLine(p, 70, 100, 85, 150);//右脚

 

运行结果:

3

 

现在我们新添加功能,建造一个比较胖的小人,那么可以按照小人写法马上模仿出来:

   1: Graphics gFat = pictureBox2.CreateGraphics();
   2:  
   3: gFat.DrawEllipse(p, 50, 20, 30, 30);
   4: gFat.DrawEllipse(p, 45, 50, 40, 50);
   5: gFat.DrawLine(p, 50, 50, 30, 100);
   6: gFat.DrawLine(p, 80, 50, 100, 100);
   7: gFat.DrawLine(p, 60, 100, 45, 150);
   8: gFat.DrawLine(p, 70, 100, 85, 150);

 

运行结果:

4

 

两者的代码及其相似,但在写代码的过程中很有可能遗漏掉一个胳膊一个腿的。很显然,画小人的时候,不管他是高矮胖瘦,都要有头、手、身体、腿,这些都是必须的,只要画就要有。不过现在还有一个更重要的问题,就是加入在别的地方用这些画小人的程序怎么办,这样是没法代码复用的。因此要把建造小人和画小人的代码分离。现在我们建两个类:

一个瘦人类:

   1: class PersonThinBuilder
   2: {
   3:     private Graphics g;
   4:     private Pen p;
   5:  
   6:     public PersonThinBuilder(Graphics g, Pen p)
   7:     {
   8:         this.g = g;
   9:         this.p = p;
  10:     }
  11:  
  12:     public void Build()
  13:     {
  14:         g.DrawEllipse(p, 50, 20, 30, 30);
  15:         g.DrawRectangle(p, 60, 50, 10, 50);
  16:         g.DrawLine(p, 60, 50, 40, 100);
  17:         g.DrawLine(p, 70, 50, 90, 100);
  18:         g.DrawLine(p, 60, 100, 45, 150);
  19:         g.DrawLine(p, 70, 100, 85, 150);
  20:     }
  21: }
 
一个胖人类:
   1: class PersonFatBuilder
   2: {
   3:     private Graphics g;
   4:     private Pen p;
   5:  
   6:     public PersonFatBuilder(Graphics g, Pen p)
   7:     {
   8:         this.g = g;
   9:         this.p = p;
  10:     }
  11:  
  12:     public void Build()
  13:     {
  14:         g.DrawEllipse(p, 50, 20, 30, 30);
  15:         g.DrawEllipse(p, 45, 50, 40, 50);
  16:         g.DrawLine(p, 50, 50, 30, 100);
  17:         g.DrawLine(p, 80, 50, 100, 100);
  18:         g.DrawLine(p, 60, 100, 45, 150);
  19:         g.DrawLine(p, 70, 100, 85, 150);
  20:     }
  21: }

 

客户端调用代码:

   1: private void button1_Click(object sender, EventArgs e)
   2: {
   3:  
   4:     Pen p = new Pen(Color.Yellow);
   5:  
   6:     Graphics gThin = pictureBox1.CreateGraphics();
   7:  
   8:     PersonThinBuilder ptb = new PersonThinBuilder(gThin, p);
   9:     ptb.Build();
  10:  
  11:     Graphics gFat = pictureBox2.CreateGraphics();
  12:  
  13:     PersonFatBuilder pfb = new PersonFatBuilder(gFat, p);
  14:     pfb.Build();
  15: }

 

运行结果同上。现在第二个问题解决了,但是还是有遗漏代码的可能,这就需要建造者模式了。可以发现,这里建造小人的“过程”是稳定的,都需要手脚,而具体建造的“细节”是不同的,有胖的有瘦的。但对于用户来说,我才不管这些,我只想告诉你,现在需要一个小胖人,你建造一个给我就行。其实,如果你需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,这就需要应用建造者模式。在建造者模式中,用户就只需要指定需要建造的类型,而具体建造的细节和过程不需要知道。

代码实现中,首先我们先定义一个抽象的建造人的类,来把建造人的这个过程稳定住,不让任何人遗忘当中的任何一步。

   1: abstract class PersonBuilder
   2: {
   3:     protected Graphics g;
   4:     protected Pen p;
   5:  
   6:     public PersonBuilder(Graphics g, Pen p)
   7:     {
   8:         this.g = g;
   9:         this.p = p;
  10:     }
  11:  
  12:     public abstract void BuildHead();
  13:     public abstract void BuildBody();
  14:     public abstract void BuildArmLeft();
  15:     public abstract void BuildArmRight();
  16:     public abstract void BuildLegLeft();
  17:     public abstract void BuildLegRight();
  18: }

 

当我们需要建造一个瘦人时,则让瘦人类去继承这个抽象类,那就必须去重写这些抽象方法,就不会被遗忘,否则编译不会通过。

   1: class PersonThinBuilder : PersonBuilder
   2: {
   3:     public PersonThinBuilder(Graphics g, Pen p)
   4:         : base(g, p)
   5:     { }
   6:  
   7:     public override void BuildHead()
   8:     {
   9:         g.DrawEllipse(p, 50, 20, 30, 30);
  10:     }
  11:  
  12:     public override void BuildBody()
  13:     {
  14:         g.DrawRectangle(p, 60, 50, 10, 50);
  15:     }
  16:  
  17:     public override void BuildArmLeft()
  18:     {
  19:         g.DrawLine(p, 60, 50, 40, 100);
  20:     }
  21:  
  22:     public override void BuildArmRight()
  23:     {
  24:         g.DrawLine(p, 70, 50, 90, 100);
  25:     }
  26:  
  27:     public override void BuildLegLeft()
  28:     {
  29:         g.DrawLine(p, 60, 100, 45, 150);
  30:     }
  31:  
  32:     public override void BuildLegRight()
  33:     {
  34:         g.DrawLine(p, 70, 100, 85, 150);
  35:     }
  36: }

 

胖人类也是一样:

   1: class PersonFatBuilder : PersonBuilder
   2: {
   3:     public PersonFatBuilder(Graphics g, Pen p)
   4:         : base(g, p)
   5:     { }
   6:  
   7:     public override void BuildHead()
   8:     {
   9:         g.DrawEllipse(p, 50, 20, 30, 30);
  10:     }
  11:  
  12:     public override void BuildBody()
  13:     {
  14:         g.DrawEllipse(p, 45, 50,40, 50);
  15:     }
  16:  
  17:     public override void BuildArmLeft()
  18:     {
  19:         g.DrawLine(p, 50, 50, 30, 100);
  20:     }
  21:  
  22:     public override void BuildArmRight()
  23:     {
  24:         g.DrawLine(p, 80, 50, 100, 100);
  25:     }
  26:  
  27:     public override void BuildLegLeft()
  28:     {
  29:         g.DrawLine(p, 60, 100, 45, 150);
  30:     }
  31:  
  32:     public override void BuildLegRight()
  33:     {
  34:         g.DrawLine(p, 70, 100, 85, 150);
  35:     }
  36: }

 

这样修改以后,在客户端还是避免不了必须知道头身手脚这些方法,因此还需要一个指挥者(Director),用它来控制建造过程,也可以用它来隔离用户与建造过程的关联。

指挥者类:

   1: class PersonDirector
   2: {
   3:     private PersonBuilder pb;
   4:     public PersonDirector(PersonBuilder pb)
   5:     {
   6:         this.pb = pb;
   7:     }
   8:  
   9:     public void CreatePerson()
  10:     {
  11:         pb.BuildHead();
  12:         pb.BuildBody();
  13:         pb.BuildArmLeft();
  14:         pb.BuildArmRight();
  15:         pb.BuildLegLeft();
  16:         pb.BuildLegRight();
  17:     }
  18: }

 

PersonDirector 类的目的就是根据用户的选择来一步一步的建造小人,而建造的过程在指挥者这里完成,用户不需要知道。而且由于这个过程每一步=都是一定要做的,那就不会出现遗漏的问题了。

客户端代码:

   1: private void button1_Click(object sender, EventArgs e)
   2: {
   3:     Pen p = new Pen(Color.Yellow);
   4:     PersonThinBuilder ptb = new PersonThinBuilder(pictureBox1.CreateGraphics(), p);
   5:     PersonDirector pdThin = new PersonDirector(ptb);
   6:     pdThin.CreatePerson();
   7:  
   8:     PersonFatBuilder pfb = new PersonFatBuilder(pictureBox2.CreateGraphics(), p);
   9:     PersonDirector pdFat = new PersonDirector(pfb);
  10:     pdFat.CreatePerson();
  11:  
  12: }

 

如果需要增加一个高个子和矮个子的小人,那么我们需要增加两个类,让他们继承 PersonBuilder ,人后客户端调用就可以了。但是如果想细化一些,比如人的五官,大腿和小腿等。这就需要权衡,如果这些细节是每个具体的小人都需要构建的,那就应该加进去,反之就没有必要。其实建造者模式是逐步建造产品的,所以建造者的 Builder 类里的那些建造方法必须足够普遍,以便为各种类型的具体建造者构造。

下面我们看看建造者模式的结构图:

5

 

建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。

建造者模式基本代码:

Product 类—产品类,由多个部件组成:

   1: class Product
   2: {
   3:     IList<string> parts = new List<string>();
   4:  
   5:     public void Add(string part)
   6:     {
   7:         parts.Add(part);
   8:     }
   9:  
  10:     public void Show()
  11:     {
  12:         Console.WriteLine("\n产品 创建 ----");
  13:         foreach (string part in parts)
  14:         {
  15:             Console.WriteLine(part);
  16:         }
  17:     }
  18: }

 

Builder 类—抽象建造者类,确定产品由两个部件 PartA 和 PartB 组成,并声明一个得到产品建造后结果的方法 GetResult :

   1: abstract class Builder
   2: {
   3:     public abstract void BuildPartA();
   4:     public abstract void BuildPartB();
   5:     public abstract Product GetResult();
   6: }

 

ConcreteBuilder1 类—具体建造者类:

   1: class ConcreteBuilder1 : Builder
   2: {
   3:     private Product product = new Product();
   4:  
   5:     public override void BuildPartA()
   6:     {
   7:         product.Add("部件A");
   8:     }
   9:  
  10:     public override void BuildPartB()
  11:     {
  12:         product.Add("部件B");
  13:     }
  14:  
  15:     public override Product GetResult()
  16:     {
  17:         return product;
  18:     }
  19: }

 

ConcreteBuilder2 类—具体建造者类:

   1: class ConcreteBuilder2 : Builder
   2: {
   3:     private Product product = new Product();
   4:     public override void BuildPartA()
   5:     {
   6:         product.Add("部件X");
   7:     }
   8:  
   9:     public override void BuildPartB()
  10:     {
  11:         product.Add("部件Y");
  12:     }
  13:  
  14:     public override Product GetResult()
  15:     {
  16:         return product;
  17:     }
  18: }

 

Director 类—指挥者类:

   1: class Director
   2: {
   3:     public void Construct(Builder builder)
   4:     {
   5:         builder.BuildPartA();
   6:         builder.BuildPartB();
   7:     }
   8: }

 

客户端代码,不需要知道具体的建造过程:

   1: static void Main(string[] args)
   2: {
   3:     Director director = new Director();
   4:     Builder b1 = new ConcreteBuilder1();
   5:     Builder b2 = new ConcreteBuilder2();
   6:  
   7:     director.Construct(b1);
   8:     Product p1 = b1.GetResult();
   9:     p1.Show();
  10:  
  11:     director.Construct(b2);
  12:     Product p2 = b2.GetResult();
  13:     p2.Show();
  14:  
  15:     Console.Read();
  16: }

 

综上,建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方法时使用的模式。

0 0
原创粉丝点击