分类:
2008-10-13 16:56:54
最近在学习Design Pattern,主要阅读的书包括:
为什么会同时读这几本书?原因很简单——因为上述任一本书都没有能把常见的Design Pattern讲清楚。
对于创建型的Pattern,我们可以这样理解。假设Client(顾客)需要获取一个Shoe(产品)对象,通常的方式是这样的:
而Pattern的引入,就是在Client与Shoe之间插入一个生产者(创造者、工厂)Factory,于是上述的一个new操作变成了一系列的调用过程:
上述代码表现出来的Client--->Factory--->Shoe之间的关系,正是这些Pattern要实现的,只是各自实现的细节和方法不一样而已。
(一)Singleton
Singleton是比较简单的,其作用就是保证对象的全局性唯一,即任一时刻只有一个实例对象存在。由其引申出的还有适用于多线程的Double-Checked Locking Pattern。下面这段C#代码是Singleton的一个典型实现:
有意者请参阅此文:
(二)Factory Method
在Factory Method中,Factory与Shoe存在一一对应的关系。这次复杂点,假设Client要获取男鞋ShoeForMale与女鞋ShoeForFemale,则必定存在均派生于Factory的FactoryMale与FactoryFemale两个工厂,其中FactoryMale为Client创建ShoeForMale对象,FactoryFemale为Client创建ShoeForFemale对象。
至于选择FactoryMale亦或FactoryFemale,则是由Client负责并选择的。一旦选定了某一Factory,就可以调用一致的factory.CreateShoe()方法来创建Shoe对象了。
(三)Abstract Factory
理解了Factory Method,我们可以把Abstract Factory看作其扩展的结果,或者说Factory Method是Abstract Factory退化的结果。与Factory Method不同的是,Abstract Factory能为Client返回的,总是多种不同的对象,同时这些对象之间总是存在Family(族)的关系,这些对象总是“同生共死”,只能生活在“同一片天空下”。这样说可能不太好理解,还是用一个通俗的例子进行说明吧。
假设这一次Client需要的是Shoe与Socks,那么Factory肯定要提供CreateShoe()与CreateSocks()两个方法为之服务。但是想想,一位男士也许不会愿意去穿一位女士的鞋或袜子吧,呵呵。所以这回对Factory得有一定的限制了,因此FactoryMale生产ShoeForMale,那么它能生产的Socks也只能是SocksForMale。同样的,FactoryFemale能生产的也只能是ShoeForFemale与SocksForFemale。
在上面这个Shoe与Socks的例子中,我们也许发现了ShoeForMale、ShoeForFemale均派生于Shoe,而SocksForMale、SocksForFemale则派生于Socks。于是我们可能会认为,Abstract Factory为Client返回的,只能是A1+B1或者A2+B2这样的产品配对,即必有A<->B这样的对应关系。而这也正体现了Abstract Factory得以适用的主要场景——在不同平台间进行移植。
但是,在GoF的书中为我们提供的例子,却出现了A1+B1、A1+C1、A2+B1这样的一些配对。即出现了Client要求A、B、C对象,而有的Factory只返回A与C,而没有B的现象。
在他的例子里,MazeFactory通过MakeMaze()、MakeRoom()、MakeWall()、MakeDoor()等接口方法能为Client提供Maze、Room、Wall与Door对象。要注意的是,Wall、Door是Room对象的组成部分,Room又成为Maze的组成部分。正是这种彼此包含的关系,体现在派生的BombedMazeFactory中后,就只实现了MakeMaze()、MakeWall()与MakeRoom()方法,而没有Door对象向Client提供。
他为什么可以这样做?或者说为什么要这样做呢?这是因为存在前述对象间的包含关系,使得Client要负责用获取的Door、Wall对象去构造Room对象,然后再用构造好的Room对象去构造Maze对象。而我之前理解的,则是认为既然是MazeFactory,必定是为Client返回好一个构造好了的Maze。正是这样的矛盾让我困惑了许久,那段时间确实很痛苦。所以就我个人认为,GoF书中这样应用Abstract Factory即便能体现Pattern适用的灵活性,却也是不严谨的。
(四)Prototype
这是一个比较简单的创建型Pattern。它就象一个仓库,里面已经有了我们需要的产品样本,我们要做的只是Clone或者说Copy一个。所以应用这个Pattern的关键,在于设计好这个样本,包括这个样本的各种Property、状态等等。然后为其设计一个恰当的接口方法Clone()。
每当Client要获取一个Shoe对象时,由于Shoe的每个实例对象已经定义好了,所以要做的只是简单地调用Shoe.Clone()而已。这有点类似于C#或者说.NET Framework下提到的“Deep Copy”(深拷贝)。
(五)Builder
个人认为,就GoF书中Maze的例子,适用Builder是最为恰当的。Builer的使用更象是搭积木,要搭的房屋是Client要的对象,其中的每一个积木块是部件对象。Builder Pattern的适用场景可以做如下模拟:
1. 有一个需要Shoe的Client;
2. Shoe是可以定制的;
3. Shoe可供定制的部件包括鞋底Tread、鞋面Vamp、鞋带Lace(甚至还有更多的可供定制的部分);
于是,Builer要做的,就是将部件的构造剥离出来,当部件构造完毕后,再将完整的Shoe交给Client。看看下面这段很简单的代码吧。
从某个角度说,Builder很象Factory,即一种Shoe对应一种Builder。同时也很象Prototype,它只是把Clone()换作了GetShoe()。
同样的,在Gof使用Builder构造Maze的示例里,他又将Maze的构造责任交给了Client。换作上述的代码,即CreateTread()、CreateVamp()与CreateBootlace()由Client直接调用。当Client获取了Tread、Vamp、Bootlace对象后,通过调用Shoe的组装函数完成组装,然后调用Builder的GetShoe()获取最后的Shoe对象。不过他的示例里,也演示了如何利用Builder进行对象计数,很是有趣。
posted on 2006-08-30 22:30 Abbey的小匣子 阅读(599)