http://www.cnblogs.com/shenfx318/archive/2007/01/28/632724.html
在设计模式的学习过程中,Builder与Factory是创建模式中两个经典的教程,给与了我们很多值得汲取的宝贵设计思想,然而Builder 与Factory模式也是初学者容易混淆的两个模式,经常看到有人问及两者的区别与适用的场景,我在近一段设计模式的学习过程中同样碰到了这个问题,在两 种模式的区别与联系间我看到的更多是后者,在这里愿意与大家分享一些我对Builder与Factory模式的感悟,有说的不对的地方,还请各位多加提 点、指教。
写在前面
本文旨在两种模式间的对比与探讨,因此还希望各位看官首先对两个模式有一定的了解为 好,因为常常看到有人提问说,Builder模式与抽象工厂(Abstract Factory)之间的区别,其实在我看来这两者间并无太多联系,因此也就谈不上区别,至于原因在此不做细述,有兴趣的朋友可以看看我写的有关抽象工厂的文章。故本文中所提的Factory模式皆指的是工厂方法(Factory Method)。
从Builder到Factory的演化
先来看看Builder模式,Builder模式的一般设计及实现
2 {
3 void BuildPart1();
4
5 void BuildPart2();
6
7 Product GetResult();
8 }
9 //ConcreteBuilderA
10 public class BuilderA : IBuilder
11 {
12 private Product product;
13
14 public void BuildPart1()
15 {
16 product = new Product();
17 product.Add("Part1 build by builderA");
18 }
19
20 public void BuildPart2()
21 {
22 product.Add("Part2 build by builderA");
23 }
24
25 public Product GetResult()
26 {
27 return product;
28 }
29 }
30 }
31 //ConcreteBuilderB
32 public class BuilderA : IBuilder
33 //
34 //Director
35 public class Director
36 {
37 public void Construct(IBuilder builder)
38 {
39 builder.BuildPart1();
40 builder.BuildPart2();
41 }
42 }
客户端调用代码
2 {
3 public void Run()
4 {
5 Director director = new Director();
6 IBuilder builder = new BuilderB();
7 director.Construct(builder);
8
9 Product product = builder.GetResult();
10 product.Show();
11 }
12 }
从类关系图上来看,Builder模式与我们熟知的工厂模式还是具有一定的区别,最 显著的莫过于这个指导者(Director)的角色,我们观察这个Director,发现他无非是以参数的形势接收了一个Builder,并按照一定的顺 序调用其相应的方法构造各个部件,使得Builder可以完成最终的产品。这其实是对复杂对象构造顺序的封装,但我们可以看到仅仅为了做这一件事情是否有 必要为它单独设计一个类?每次都要实例化这个类的对象呢?既然建造顺序是相对稳定的,而且对于客户来讲并不关心这个顺序,那么是否可以将它与 Builder类结合?当然可以,实际中也确实常常进行这样的简化,比如StringBuilder类,我们看不到类似Director对象的存在及调 用。好,那么经过我们一次的改造以后,变成了如下形式。
客户端调用代码
2 {
3 public void Run()
4 {
5 IBuilder builder = new BuilderB();
6 builder.Construct(); //Attention here!
7
8 Product product = builder.GetResult();
9 product.Show();
10 }
11 }
再看看客户端中的这条Builder.Construct()语句,似乎也有些多余 了,客户一般只有在需要产品的时候才会实例化一个Builder对象,因此对于客户来讲,他创建了Builder意味着他需要Builder能够为他生成 一个产品(GetProduct),而返回产品必然需要构造Construct,于是我们又可以对代码进一步简化,将Construct方法与 GetProduct方法结合。
2 {
3 private Product product;
4
5 private void BuildPart1()
6 {
7 product = new Product();
8 product.Add("Part1 build by builderA");
9 }
10
11 private void BuildPart2()
12 {
13 product.Add("Part2 build by builderA");
14 }
15
16 public Product GetResult()
17 {
18 //Construct here!
19 BuildPart1();
20 BuildPart2();
21
22 return product;
23 }
24 }
对 了,客户是不关心这个复杂对象的建造生成过程的,也就是说BuildPartN(),这些方法对于客户是没有意义的,是不可见的,那么我们就将其声明为 private,而GetProduct只是方法的一个名称,叫什么都可以,你可以叫GetResult,ReturnProduct….把它称为 Create亦可。OK,之后再来看看改造后的类图。
OMG! 从图上来看,除了名称叫做Builder外,其他根本和Factory模式没有什么区别,从代码来看,不过是工厂模式在返回具体的产品前对该产品进行了一些初始化的工作。
2 public Product Create()
3 {
4 BuildPart1(); // Initail part1 of product
5 BuildPart2(); // Initail part2 of product
6
7 return product;
8 }
就是这些代码,我们将其挪个地方改个名称又何尝不可呢?
2 public Product Create()
3 {
4 return product;
5 }
6 //Build job move to the product class
7 public class Product
8 {
9 ArrayList parts = new ArrayList();
10
11 public Product()
12 {
13 InitalPart1(); // Same as BuildPart1()
14 InitalPart2(); // Same as BuildPart2()
15 }
16 }
好了,通过对Builder模式向Factory的一步步演化,我们可以看到两者实质上并没有太多的区别,这也就是本文想要阐述的观点,也许很多朋友这时会反驳我了,说两者怎么会没有区别呢?
- Builder模式用于创建复杂的对象。
- 对象内部构建间的建造顺序通常是稳定的。
- 对象内部的构建通常面临着复杂的变化。
对于持以上观点的朋友,我也有如下一些疑问。
- 什么样的对象属于复杂的对象?关于对象复杂与否是如何划分的?
- 站在客户的角度来讲,是否关心对象的复杂程度及建造顺序?
- 既然客户不关心对象是否复杂以及生成的顺序,那么将这个复杂对象分布构建的意义就在于它有益于设计方了,对于设计人员,复杂对象分布构建的分布体现在哪里?是依次写几行代码还是依次调用几个方法?
- 这种分布给你带来了哪些好处?可以帮助你应付哪些变化?
既然我们不是为了学习设计模式而学习,而是为了学习OOD的精髓,能够编写出更加灵活,适用于需求变化的软件。那么对于需求变化,我们不妨再来看看两个模式是如何应对的。Builder模式适用场景中的第3条提到了"变化"二字:对象内部的构建通常面临着复杂的变化。 就拿PartA为例,现在这个对象发生了剧烈的变化,对于Builder来讲,修改BuildPartA()方法显然是违反OCP的,于是采取第二种方 法,从抽象Builder派生一个新的NewBuilder类,为这个新的Builder添加变化后的BuildPartA()方法,其余 BuildPart方法不变。代码如下。
2 {
3 private Product product;
4 #region IBuilder Members
5
6 public void BuildPart1()
7 {
8 //With new part1.
9 product = new Product();
10 product.Add("NewPart1 build by builderA");
11 }
12
13 public void BuildPart2()
14 {
15 //Nothing changed.
16 product.Add("Part2 build by builderA");
17 }
18
19