第十一课 建造者模式
接着模板方法模式来讲建造者吧。其实开始我也有点模糊,看着建造者模式很想模板方法。为什么呢,这里引用一段李会军博客里的一段概述:
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式。
这里看到哦,“将对象各个部分组合起来的算法相对稳定”,这就立马让我想到了模板方法模式。这里这样处理无非就是使用模板定义固定的对象的建造流程吗。我在线给李会军同志提出了我的疑问。他的回答如下:
@王文斌
其实它们还是有区别的,建造者模式关注的是“对象创建”的过程,而模板方法关注的是行为!就这个例子来说,用哪种方法实现都没有问题,又或者可以结合它们来实现。另外,只要实现的优雅,也不用完全追求去符合某一个模式,那样反而有过度设计之嫌。
其实回头我又仔细看了一下java与模式,里面分析了模板方法与建造者间的关系。个人感觉,其实建造者模式就是利用了模板方法,正如上面说的。建造者用的时候,是为了解决构造复杂对象。这些复杂对象往往对创建顺序等逻辑有特殊要求,但是逻辑是有共通点存在的,所以利用了模板方法的处理方式。把“创建”这一行为单独抽取出来,定义模板,这样就把共通逻辑拿出来了。但是创建者最终目的是要创建对象返回,就像李会军同志给解答的,建造者模式关注的是“对象创建”的过程。最终目的是要创建一个对象。我觉得完全可以理解为模板方法的一个特殊用法。呵呵,废话半天,来看个实例吧。(出自大话设计模式)
(话说csdn都不让上传附件吗。。。)
首先介绍一下,这是一个winform的程序实例,实现2个小人的绘制。
首先是建造者实现:(我考虑先把全部代码贴出来,然后再分析,方便大家考出来运行)
1. using System;
2. using System.Collections.Generic;
3. using System.Text;
4. using System.Drawing;
5. using System.Reflection;
6.
7. namespace 建造者模式
8. {
9. abstract class PersonBuilder
10. {
11. protected Graphics g;
12. protected Pen p;
13.
14. public PersonBuilder(Graphics g, Pen p)
15. {
16. this.g = g;
17. this.p = p;
18. }
19.
20. public abstract void BuildHead();
21. public abstract void BuildBody();
22. public abstract void BuildArmLeft();
23. public abstract void BuildArmRight();
24. public abstract void BuildLegLeft();
25. public abstract void BuildLegRight();
26. }
27.
28. class PersonThinBuilder : PersonBuilder
29. {
30. public PersonThinBuilder(Graphics g, Pen p)
31. : base(g, p)
32. { }
33.
34. public override void BuildHead()
35. {
36. g.DrawEllipse(p, 50, 20, 30, 30);
37. }
38.
39. public override void BuildBody()
40. {
41. g.DrawRectangle(p, 60, 50, 10, 50);
42. }
43.
44. public override void BuildArmLeft()
45. {
46. g.DrawLine(p, 60, 50, 40, 100);
47. }
48.
49. public override void BuildArmRight()
50. {
51. g.DrawLine(p, 70, 50, 90, 100);
52. }
53.
54. public override void BuildLegLeft()
55. {
56. g.DrawLine(p, 60, 100, 45, 150);
57. }
58.
59. public override void BuildLegRight()
60. {
61. g.DrawLine(p, 70, 100, 85, 150);
62. }
63. }
64.
65. class PersonFatBuilder : PersonBuilder
66. {
67. public PersonFatBuilder(Graphics g, Pen p)
68. : base(g, p)
69. { }
70.
71. public override void BuildHead()
72. {
73. g.DrawEllipse(p, 50, 20, 30, 30);
74. }
75.
76. public override void BuildBody()
77. {
78. g.DrawEllipse(p, 45, 50, 40, 50);
79. }
80.
81. public override void BuildArmLeft()
82. {
83. g.DrawLine(p, 50, 50, 30, 100);
84. }
85.
86. public override void BuildArmRight()
87. {
88. g.DrawLine(p, 80, 50, 100, 100);
89. }
90.
91. public override void BuildLegLeft()
92. {
93. g.DrawLine(p, 60, 100, 45, 150);
94. }
95.
96. public override void BuildLegRight()
97. {
98. g.DrawLine(p, 70, 100, 85, 150);
99. }
100. }
101.
102. class PersonDirector
103. {
104. private PersonBuilder pb;
105. public PersonDirector(string type, Graphics g, Pen p)
106. {
107. string assemblyName="建造者模式";
108. object[] args = new object[2];
109. args[0] = g;
110. args[1] = p;
111.
112. this.pb = (PersonBuilder)Assembly.Load(assemblyName).CreateInstance(assemblyName+".Person" + type + "Builder", false, BindingFlags.Default, null, args, null, null);
113. }
114.
115. public void CreatePerson()
116. {
117. pb.BuildHead();
118. pb.BuildBody();
119. pb.BuildArmLeft();
120. pb.BuildArmRight();
121. pb.BuildLegLeft();
122. pb.BuildLegRight();
123. }
124. }
125. }
126.
窗体代码:
1. using System;
2. using System.Collections.Generic;
3. using System.ComponentModel;
4. using System.Data;
5. using System.Drawing;
6. using System.Text;
7. using System.Windows.Forms;
8.
9. namespace 建造者模式
10. {
11. public partial class Form1 : Form
12. {
13. public Form1()
14. {
15. InitializeComponent();
16. }
17.
18. private void Form1_Load(object sender, EventArgs e)
19. {
20. }
21.
22. private void button1_Click(object sender, EventArgs e)
23. {
24. Pen p = new Pen(Color.Yellow);
25. PersonDirector pdThin = new PersonDirector("Thin",pictureBox1.CreateGraphics(),p);
26. pdThin.CreatePerson();
27.
28. PersonDirector pdFat = new PersonDirector("Fat", pictureBox2.CreateGraphics(), p);
29. pdFat.CreatePerson();
30. }
31. }
32. }
窗体设计代码:
1. namespace 建造者模式
2. {
3. partial class Form1
4. {
5. /// <summary>
6. /// 必需的设计器变量。
7. /// </summary>
8. private System.ComponentModel.IContainer components = null;
9.
10. /// <summary>
11. /// 清理所有正在使用的资源。
12. /// </summary>
13. /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
14. protected override void Dispose(bool disposing)
15. {
16. if (disposing && (components != null))
17. {
18. components.Dispose();
19. }
20. base.Dispose(disposing);
21. }
22.
23. #region Windows 窗体设计器生成的代码
24.
25. /// <summary>
26. /// 设计器支持所需的方法 - 不要
27. /// 使用代码编辑器修改此方法的内容。
28. /// </summary>
29. private void InitializeComponent()
30. {
31. this.button1 = new System.Windows.Forms.Button();
32. this.pictureBox1 = new System.Windows.Forms.PictureBox();
33. this.pictureBox2 = new System.Windows.Forms.PictureBox();
34. ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
35. ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
36. this.SuspendLayout();
37. //
38. // button1
39. //
40. this.button1.Location = new System.Drawing.Point(300, 12);
41. this.button1.Name = "button1";
42. this.button1.Size = new System.Drawing.Size(75, 23);
43. this.button1.TabIndex = 0;
44. this.button1.Text = "button1";
45. this.button1.UseVisualStyleBackColor = true;
46. this.button1.Click += new System.EventHandler(this.button1_Click);
47. //
48. // pictureBox1
49. //
50. this.pictureBox1.BackColor = System.Drawing.Color.Black;
51. this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
52. this.pictureBox1.Location = new System.Drawing.Point(12, 12);
53. this.pictureBox1.Name = "pictureBox1";
54. this.pictureBox1.Size = new System.Drawing.Size(141, 184);
55. this.pictureBox1.TabIndex = 1;
56. this.pictureBox1.TabStop = false;
57. //
58. // pictureBox2
59. //
60. this.pictureBox2.BackColor = System.Drawing.Color.Black;
61. this.pictureBox2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
62. this.pictureBox2.Location = new System.Drawing.Point(159, 12);
63. this.pictureBox2.Name = "pictureBox2";
64. this.pictureBox2.Size = new System.Drawing.Size(135, 184);
65. this.pictureBox2.TabIndex = 2;
66. this.pictureBox2.TabStop = false;
67. //
68. // Form1
69. //
70. this.AutoScaleDimensions = new System.Drawing.SizeF( 6F , 12F );
71. this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
72. this.ClientSize = new System.Drawing.Size(381, 208);
73. this.Controls.Add(this.pictureBox2);
74. this.Controls.Add(this.pictureBox1);
75. this.Controls.Add(this.button1);
76. this.Name = "Form1";
77. this.Text = "Form1";
78. this.Load += new System.EventHandler(this.Form1_Load);
79. ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
80. ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
81. this.ResumeLayout(false);
82.
83. }
84.
85. #endregion
86.
87. private System.Windows.Forms.Button button1;
88. private System.Windows.Forms.PictureBox pictureBox1;
89. private System.Windows.Forms.PictureBox pictureBox2;
90.
91. }
92. }
93.
94.
最后程序入口:
1. using System;
2. using System.Collections.Generic;
3. using System.Windows.Forms;
4.
5. namespace 建造者模式
6. {
7. static class Program
8. {
9. /// <summary>
10. /// 应用程序的主入口点。
11. /// </summary>
12. [STAThread]
13. static void Main ()
14. {
15. Application.EnableVisualStyles();
16. Application.SetCompatibleTextRenderingDefault(false);
17. Application.Run(new Form1());
18. }
19. }
20. }
好了,看下效果图:
点击按钮:
下面开始分析这个实例。
其实我本来不打算用UML图说话呢,毕竟很多人不会看(别砸我),但是这里还是有必要贴一下的,偷了下懒,直接引用李会军博客上的图了。
首先大家可以看到,从左到右的四个角色。
1. 客户端
2. 指导者
3. 建造者
4. 产品
这就是建造者模式实现所牵扯的4个角色(当然有的时候根据需要可以合并指导者和建造者,这里描述一般实现)。
首先客户端是调用的喽,上例就是窗体了。
然后指导者起到一个管理这建筑人员的作用,产品的建造(其实就是初始化)要遵循他的要求来。
再来是建造者,工程实施人员,负责具体建造对象。
最后产品,就是要建造的对象。(要初始化的对象哦)
把上例套入这个图。(这里暂时不考虑客户端)
产品??好像没体现出来,呵呵,其实就是最终画出来的两个小人。谁规定产品一定要是一个类对象哦。
建造者 Builder:
PersonBuilder 抽象建筑者基类,定义了所有部件的建造方法。(头,身子,胳膊,腿)
ConcreteBuilder:
PersonThinBuilder
PersonFatBuilder
这两个是建筑者的具体实现类。
指导者 Director:
PersonDirector
这个是指导者。
其实大家看出来了没有,Builder的实现方法看着眼熟吗?是不是很向抽象工厂?特别是指导者的实现,就是通过反射返回具体工厂对象不是?不过是用来初始化自己的建造者对象了,没有返回罢了。
然后CreatePerson是具体的建造方法了。来看下面:
pb.BuildHead(); //建造头
pb.BuildBody(); //建造身体
pb.BuildArmLeft(); //建造左手
pb.BuildArmRight(); //建造右手
pb.BuildLegLeft(); //建造左腿
pb.BuildLegRight(); //建造右腿
其实就是个模板方式吧。就是前面说得创建对象的复杂逻辑了。这个地方很可能对具体建造顺序有要求的。其实就本例来说,一般咱们画小人也是先画头再画手脚不是吗?
具体胳膊手就是简单的绘图方法,这里不提了。再来看客户端的调用。
1. private void button1_Click(object sender, EventArgs e)
2. {
3. Pen p = new Pen(Color.Yellow);
4. PersonDirector pdThin = new PersonDirector("Thin",pictureBox1.CreateGraphics(),p);
5. pdThin.CreatePerson();
6.
7. PersonDirector pdFat = new PersonDirector("Fat", pictureBox2.CreateGraphics(), p);
8. pdFat.CreatePerson();
9. }
首先新建一个画笔(不要问为什么,自己google)。
然后创建指导者对象各一个(一胖一瘦,看着其实也像工厂方法吧)
然后分别调用指导者的建造方法。(其实就是模板方法来初始化哦)
然后就画出那两个小人了。
其实这个例子除去没有很好的体现出产品以外,算是很经典地实例了。Java与模式中还提到了JMail的用法,这里有兴趣的自己去查看一下。也是经典实例。
建造者模式与工厂模式同样都是为了创建对象的设计模式,所以实际应用中两者经常结合使用。这里可以看出来,其实设计模式没必要非要把界限划分的那么清晰。就像李会军同志教育我的一样,不要“过度设计”,呵呵,就是不要为了模式而模式。
以上
作者:王文斌
转载请注明出处
感谢李会军同志指导。(呵呵,叫同志好像有打官腔的味道哦)
晕,图片上传给我传到哪里去了,真不如原来的编辑器好用。那两个图大家自己运行下吧。