Builder与Factory,殊途同归!

在设计模式的学习过程中,Builder与Factory是创建模式中两个经典的教程,给与了我们很多值得汲取的宝贵设计思想,然而Builder与Factory模式也是初学者容易混淆的两个模式,经常看到有人问及两者的区别与适用的场景,我在近一段设计模式的学习过程中同样碰到了这个问题,在两种模式的区别与联系间我看到的更多是后者,在这里愿意与大家分享一些我对Builder与Factory模式的感悟,有说的不对的地方,还请各位多加提点、指教。

写在前面

本文旨在两种模式间的对比与探讨,因此还希望各位看官首先对两个模式有一定的了解为好,因为常常看到有人提问说,Builder模式与抽象工厂(Abstract Factory)之间的区别,其实在我看来这两者间并无太多联系,因此也就谈不上区别,至于原因在此不做细述,有兴趣的朋友可以看看我写的有关抽象工厂的文章。故本文中所提的Factory模式皆指的是工厂方法(Factory Method)。

从Builder到Factory的演化

先来看看Builder模式,Builder模式的一般设计及实现
 

 1     public interface IBuilder
 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     }

客户端调用代码

 1     public class Client
 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对象的存在及调用。好,那么经过我们一次的改造以后,变成了如下形式。


客户端调用代码

 1     public class Client
 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方法结合。 

 1     public class BuilderA : IBuilder
 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模式没有什么区别,从代码来看,不过是工厂模式在返回具体的产品前对该产品进行了一些初始化的工作。 

1         //Create method in Buider
2         public Product Create()
3         {
4             BuildPart1(); // Initail part1 of product
5             BuildPart2(); // Initail part2 of product
6 
7             return product;
8         }

就是这些代码,我们将其挪个地方改个名称又何尝不可呢? 

 1         //Create method in Buider
 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的一步步演化,我们可以看到两者实质上并没有太多的区别,这也就是本文想要阐述的观点,也许很多朋友这时会反驳我了,说两者怎么会没有区别呢?

  1. Builder模式用于创建复杂的对象。
  2. 对象内部构建间的建造顺序通常是稳定的。
  3. 对象内部的构建通常面临着复杂的变化。

对于持以上观点的朋友,我也有如下一些疑问。

  1. 什么样的对象属于复杂的对象?关于对象复杂与否是如何划分的?
  2. 站在客户的角度来讲,是否关心对象的复杂程度及建造顺序?
  3. 既然客户不关心对象是否复杂以及生成的顺序,那么将这个复杂对象分布构建的意义就在于它有益于设计方了,对于设计人员,复杂对象分布构建的分布体现在哪里?是依次写几行代码还是依次调用几个方法?
  4. 这种分布给你带来了哪些好处?可以帮助你应付哪些变化?

既然我们不是为了学习设计模式而学习,而是为了学习OOD的精髓,能够编写出更加灵活,适用于需求变化的软件。那么对于需求变化,我们不妨再来看看两个模式是如何应对的。Builder模式适用场景中的第3条提到了"变化"二字:对象内部的构建通常面临着复杂的变化。就拿PartA为例,现在这个对象发生了剧烈的变化,对于Builder来讲,修改BuildPartA()方法显然是违反OCP的,于是采取第二种方法,从抽象Builder派生一个新的NewBuilder类,为这个新的Builder添加变化后的BuildPartA()方法,其余BuildPart方法不变。代码如下。 

 1     public class NewBuilder : IBuilder
 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         #endregion
20 
21         public Product GetResult()
22         {
23             return product;
24         }
25     }

恩,上面的场景对于Builder模式的使用,还算比较恰当。如果我们要换成Factory呢?一样可以通过扩展来应对变化。 

 1     public class NewFactory : Factory
 2     {
 3         Product product = null;
 4 
 5         public Product Create()
 6         {
 7             //With new part1.
 8             product = new Product();
 9             product.Add("NewPart1 build by builderA");
10 
11             //Nothing changed.
12             product.Add("Part2 build by builderA");
13             return product;
14         }
15     }

你可能会认为,我改造后的Factory就不叫Factory了,已经失去了Factory的本意,好吧,那我们暂且抛开它的名称,换个角度来看看Builder与Factory,Builder具有Factory应付不了的情况吗?没有!因为对象很复杂,所以使用Builder构建对象功能更强大,更具有灵活性吗?没有!客户对于取得产品的过程,以及最终产品的使用有区别吗?没有!因此Builder仅仅是在代码的结构上与Factory产生了一些异同,使得用户可以在取得产品前对产品进行一定的初始化工作。如果这也能够称为新模式的话,那么只能说个人对于设计间区别的理解不同。

还有最后要说的一点,关于建造者模式中第2条对象内部构建间的建造顺序通常是稳定的,这点在我看来也难以构成对于与Factory模式区别的理由,因为Factory模式从来就没有考虑对象的建造顺序!只有不稳定的东西才能带来变化的可能性,将稳定的不会变化的东西也作为设计模式的理由是否有些牵强了?  

本文仅代表了作者当时的认知程度与观点,文中之所以主要强调了两个模式间的相同点还是因为本人的水平有限未能找到合适的例子来区分二者,还希望大家各抒己见,为在下答疑解惑。

posted on 2007-01-28 22:19 shenfx 阅读(2411) 评论(15)  编辑 收藏 所属分类: OO Design&Pattern

评论

#1楼  2007-01-29 09:00 革命老前辈 [未注册用户]

没有什么固定模式,自己喜欢什么,那个顺手就哪个   回复  引用    

#2楼  2007-01-29 09:03 mj [未注册用户]

我觉得builder和factory还是有区别的,关键就在于director封装了对象内部的构建顺序

当对象内部的构建顺序相当稳定,而每个部件的建造却又千差万别的时候,用bulider模式可以封装组装的顺序,于是当需要扩展时就只需要重写各个部件即可。而用factory模式则需要关心组装的顺序,这在产品组成较复杂而需要经常扩展时将不利于扩展

  回复  引用    

#3楼  2007-01-29 09:19 蛙蛙池塘      

博客园现在果然快多了   回复  引用  查看    

#4楼  2007-01-29 09:34 BirdsHover      

我也感觉快多了,我在家电信原本很快,现在公司网通速度也很快了   回复  引用  查看    

#5楼 [楼主] 2007-01-29 10:02 shenfx      

........ 各位跑这里交流网速来了-_-#   回复  引用  查看    

#6楼  2007-01-29 11:20 Icebird      

模式本来就是可以相互转化的,关键在于你看问题的角度。
  回复  引用  查看    

#7楼  2007-02-07 12:56 zhuxi [未注册用户]

我是游客,能回复吗?   回复  引用    

#8楼  2007-02-07 13:18 zhuxi [未注册用户]

能回复,太好了!

看了你的几篇关于设计模式的文章,感觉你是
一个很会思考的人!
《殊途同归》这篇文章很是标新立异,
但标新立异是在对原有事物深刻思考的基础之上
才有可能的。
我现在也在看设计模式方面的书,但自己的头脑很混乱,
稀里糊涂的,没有头绪。因此,真的很佩服你的思考能力。

我看过你的论述之后,很有收获,让我搞清楚了一些疑惑:
即这两种模式有什么用,在哪里使用工厂和生成器模式?

我认为工厂模式和生成器模式是有区别的:
工厂模式强调的是产品;
生成器模式强调的是生产过程;

工厂生产的产品之间有一定的联系,这种联系是工厂模式应用的原因;
而生成器模式并不在乎产品是什么,只要产品的生产过程相同,
则就可以用同样的代码来生成。


这就是我从你的文章里得到收获:通过区别这两种模式,
我清楚了工厂和生成器的使用场合。

真的很谢谢你。


我的思考也是很肤浅的,希望得到指正。
我已经收藏了你的网页,会经常来看你的文章的。


呵呵!!   回复  引用    

#9楼  2007-02-08 09:10 zhuxi [未注册用户]

从昨天到现在,我想了一天,也没想到比较贴切的描述生成器的例子,
不知道夜淡茶清有好的例子没有?有的话,希望你能给我讲讲。

生成器的关注点是:生产过程相同,即同样的过程也可以生产出差别很大的产品,现实中有这样的例子吗?我想象不到啊。

还有,
我感觉生成器和工厂的关系,就像是接口和基类的关系:
接口规定行为,生成器固定了行为的顺序;基类规定了一类产品的特性,
工厂能生产某一类产品。生成器和工厂分别是接口和基类的实现。

不知道我的这种感觉对不对?请赐教。

呵呵。   回复  引用    

#10楼 [楼主] 2007-02-09 10:22 shenfx      

@zhuxi
谢谢您的回复,对我来说是真的是很大的鼓励!其实我也是才学习设计模式不久的,不敢说赐教,只能说跟您探讨一些我在学习过程中的感悟。
---------------------------------------------------
1.关于您说的描述生成器的例子,其实想象生产汽车的过程,在汽车工厂中对于不同牌子的汽车的组装过程都是一样的,先装车顶、后装车门、再装轮子,这个相对稳定的建造顺序建造出来的是抽象产品--汽车,而对于实际中生产出来的具体汽车有可能是奔驰、有可能是宝马还有可能使OOOO(奥迪),这是具体产品间的不同表象。
2.您说的生成器和工厂的关系,就像是接口和基类的关系,其实再某种程度上来说确实是这样的。
工厂模式更加注重的是对于实现相同功能的不同产品的封装。
生成器模式注重的是对于相同产品不同表象的封装。
而对于功能来讲,更加适合采用接口来实现;对于表象来讲,更加近似于类的属性,比如宝马、奔驰只不过是汽车的品牌属性,更加适用于基类实现。
---------------------------------------------------
由于现在在上班呢,时间有限,就先跟您交流到这里吧,最后还是衷心感谢您的回复。   回复  引用  查看    

#11楼  2007-04-05 15:50 _迷糊 [未注册用户]

我觉得LZ犯了一个最大的错误就是:把模式和具体的应用混为一谈了!

什么样的对象属于复杂的对象?关于对象复杂与否是如何划分的?
站在客户的角度来讲,是否关心对象的复杂程度及建造顺序?
既然客户不关心对象是否复杂以及生成的顺序,那么将这个复杂对象分布构建的意义就在于它有益于设计方了,对于设计人员,复杂对象分布构建的分布体现在哪里?是依次写几行代码还是依次调用几个方法?
这种分布给你带来了哪些好处?可以帮助你应付哪些变化?

以上你的所有反问都是针对具体的应用提出的!
但问题是我们讨论的是一个在N多个具体应用的基础上提炼出来的设计模式!
什么是模式呢?就是可以适用一类问题的经典的总结!
------------------
我是个初学者,可能对问题的看法过于片面。
欢迎LZ和其他达人提出不同观点!
mailto:sammy_chan@163.com   回复  引用    

#12楼 [楼主] 2007-04-05 22:51 shenfx      

@_迷糊
难道您不认为设计模式就是为了解决具体应用而来的吗?
既然设计模式来源与对于具体应用的解决方案的抽象,那么将其反之应用回去解决具体问题应当亦可。   回复  引用  查看    

#13楼  2007-05-08 15:16 context [未注册用户]

工厂模式相当于整个工厂,生成器模式相当于生产线。
一个工厂里有多条生产线,同样的产品可以经过不同的生产线成为不同的产品。   回复  引用    

#14楼  2007-10-18 15:45 iWorm      

我觉得, 这个Builder引入了Director是关键.
就像造汽车, 汽车生产厂不会自己造轮胎, 不会自己造座椅. 说的极端一点, 汽车生产实际就是一个组装的过程, 汽车的各个零件都是从其他工厂制造出来的.

这里的汽车工厂实际上就是一个Director, 而各个制造零件的工厂才是Factory. Director可以选择从不同工厂采购, 也可以按照不同的顺序建造.

我觉得这就是Builder模式和Factory模式的区别.   回复  引用  查看    

#15楼  2008-04-11 16:51 雄健      

很赞同iWorm的例子
从采购制造零件的角度讲,汽车工厂是个Factory
从装配制造零件的角度讲,汽车工厂就是个Director   回复  引用  查看    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-01-28 23:03 编辑过
 
 


导航

公告

Visitor Counter
SmartBargains Coupons

版权声明:本Blog旨在技术交流,文章可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
<2007年1月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

统计

与我联系

搜索

 

常用链接

随笔分类

随笔档案

积分与排名