FactoryMethod是一個相對比較簡單的創建型模式,但是能領悟或者用對的并不多見;很多示例都沒有反應出Factory Method的核心思想,只是實現了一個簡化版的Abstract Factory,然后給出的解釋是Factory Method模式解決“單個對象”的需求變化,Abstract Factory 模式解決“系列對象”的需求變化。
試想一下,如果把1視為N的一種特殊情況,則一個產品系列可能只包含一個對象;那么我們是不是可以認為Factory Method是一個簡化版的Abstract Factory呢?實際上,Factory Method模式與Abstract Factory模式雖然同屬于對象創建型模式,并且AbstractFactory類通常用Factory Method模式實現,并且效果上都可用于連接平行的類層次,但是這兩個模式在思想上有著本質的區別。網上的文章抄來抄去,結果錯誤也被到處傳。上一篇介紹了被普遍誤用的Builder模式,這篇繼續為Factory Method正名。
1. Factory Method (Form《設計模式》 GOF)
1.1 意圖
定義一個用于創建對象的接口,讓子類來決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。
1.2 別名
虛構造器(Virtual Constructor)
1.3 實用性
在同時滿足下列情況下可以使用Factory Method模式:
- 當一個類不知道他所必須創建的類的對象的時候;
- 當一個類希望由它的子類來指定他所創建的對象的時候;
- 當類將創建對象的職責委托給多個幫助子類中的某一個,并且你希望將哪一個幫助子類是代理者這一信息局部化的時候。(這個翻譯很汗,英文原文是:classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.)
1.4 結構:(直接從書中截的圖)
1.5 補充說明
- 上面是GOF對FactoryMethod的闡述,字里行間,都流露出繼承思想:父類(Creator)不知道他所必須創建的類的對象(Product),于是就留了個接口,委托給子類(ConcreteCreator)來實現(重寫)。
- Creator類中還有個AnOperation方法,GOF還專門為這個方法做了注釋,該方法是一個Template Method(模板方法),其調用FactoryMethod()。并且在相關模式中,GOF也重復提到:“工廠方法通常在Template Method中被調用”,很多介紹Factory Method的例子,都直接無視這個Method和注釋,導致誤用Factory Method模式。
- 這個類圖中,沒有畫“Client”類,而Abstract Factory中,有畫Client類。這里應該不是高興的時候就畫,不高興的時候就不畫,畫與不畫自有其中的道理。
繼續沿用上一篇的思路,從幾個被曲解的例子開始。
2. 兩個簡化版的Abstract Factory模式
2.1 Abstract Factory模式結構圖
(直接從書中截的圖)
2.2 當產品系列中只包含一個對象
Abstract Factory 用于創建產品系列,一個系列的產品可能包含多個(N,N>=1)個對象;但如果我們把1視為N的一種特殊情況,則一個產品系列可能只包含一個對象,則有如下結構:
1. 《大話設計模式》中Factory Method的例子:(從書中截的圖)
2. <.Net設計模式(5):工廠方法模式(Factory Method)>
http://www.rzrgm.cn/Terrylee/archive/2006/01/04/310716.html, 直接引用的原文中的兩個圖片:
由于是兩個圖,我也沒有做處理,直接搬過來了;畫上聚合線的話,與前面的例子差不多。
2.3 對這兩個例子的說明
GOF在介紹Abstract Factory模式的時候,有說到“AbstractFactory類通常用Factory Method模式實現,但它們也可以用Prototype實現”。雖然上面兩個例子中,只看其中的局部部分的話,用到了Factory Method模式,但結合原作者們的闡述、及擴展點,全局地看,并沒有反映出Factory Method的思想。
3. Factory Method與Abstract Factory的區別
Factory Method模式與Abstract Factory模式雖然同屬于對象創建型模式,并且AbstractFactory類通常用Factory Method模式實現,并且效果上都可用于連接平行的類層次(Factory Method不限于此),但是這兩個模式在思想上有著本質的區別。我理解的Factory Method與Abstract Factory,有如下幾點區別:
3.1 對象職責上:Abstract Factory中的Factory,只具有創建對象(一個產品系列)的唯一職責;而Factory Method中的Creator,往往具有實際的邏輯和意義
回過頭來看Factory Method的結構圖,可以看到Creator類中還有個AnOperation方法,GOF還專門為這個方法做了注釋,該方法是一個Template Method(模板方法),其調用FactoryMethod()。并且在相關模式中,GOF也重復提到:“工廠方法通常在Template Method中被調用”。也就是說,通常是在Creator類自己的其他方法里面,調用Factory Method方法。為啥會這樣子呢?
GOF用下面這個例子來引出Factory Method模式:
這個例子中,Application扮演Creator的角色,MyApplication扮演ConcreteCreator的角色。Application是一個鮮活的類,它是對具體事物(應用)的抽象,具有自己的職責,而不僅僅只是new一個Document對象;它的NewDocument方法(Template Method)調用CreateDocument,Application對象其實就是Document對象的使用者(Client);Factory Method突出的是對Procuct(本例中的Document)創建,所以在結構圖中,沒有出現額外的Client類,也不需要出現。
(2010-09-29補充 [特別感謝一樓的soudog] :) Factory Method的creator同時包含了不變的代碼邏輯和變化部分,但里面的變化部分可以委托子類來重寫(override);而abstract facotry把變化部分提取到factory類中,將不變的代碼邏輯(Client)與變化部分分離得更徹底,然后通過聚合來連接Client和factory。
3.2 擴展上:Abstract Factory側重水平擴展,而Factory Method側重垂直擴展
“水平擴展”、“垂直擴展”的概念是我自己胡口亂說的,呵呵。可以用下面兩個圖來說明:
先看Abstact Factory(留意圖中的紅色箭頭及方向):
Abstract Factory:不同的ConcreteFactory,創建不同的產品系列,Factory之間可以相互替換,從而替換產品系列。Client面向接口(AbstractFactory和AbstractProductA)編程,Factory類封裝了對象系列的創建工作,具體的產品也從Client中分離開來,使得我們很容易交換產品系列。圖中我們可以看到,新增加的ProductA3/B3和ConcreteFactory3,與原有ConcreteProduct和ConcreteFactory,處于平行的類層次。這就是我所謂的“水平擴展”-_-
補充:雖然可以增加其他的ConcreteFactory,譬如ConcreteFactory4,讓其繼承現有的ConcreteFactory,看起來ConcreteFactory4與其他的ConcreteFacotry不再屬于同一個類層次,看起來不再“水平”了。但是,這里強調的是Client的視角,從Client看來,ConcreteFactory4與ConcreteFactory1~3,從意義上是等價的,即創建一個系列的產品,且他們之間可以相互替換,以實現替換產品系列。即:從邏輯意義上看,還是可以看作“水平擴展”。
再看Factory Method(留意圖中的深綠色箭頭及方向)::
Factory Method:子類(ConcreteCreator)重寫父類(Creator)的創建產品接口(FactoryMethod())。結合Factory Method的實用性及Application/Document的例子,ConcreteCreator該創建什么類型的Product,是依賴于ConcreteCreator自身封裝的邏輯來決定的(上一節介紹了,Creator/ConcreteCreator是具有現實意義的類),這里強調的是ConcreteCreator與Creator之間的繼承關系,它們處于不同的類層次,這就是我所謂的“垂直擴展”。
補充:雖然可以增加ConcreteCreator3/ConcreteProduct3,如上圖所示,讓ConcreteCreator之間看起來不再垂直。但是,這里強調的是Creator與ConcreteCeator、以及Product與ConcreteProduct之間的關系。從Creator的Client(圖中沒有畫出)來看,不同的ConcreteCreator之間是不能替換的。譬如Application/Document的例子,當Client操作的是一個繪圖應用,則使用的必然是DrawingApplication和DrawingDocument,而不可能是TextApplication和TextDocument。雖然在“效果”一節中,GOF闡述到:Factory Method可以連接平行的類層次,并以Figure和Manipulator為例,如下圖所示:
從圖形結構上看,Creator(LineFigure和TextFigure等),與Product(LineManipulator和TextManipulator)之間是處于平行的類層次。但是值得注意的是:Product之間、TextManipulator之間,是不可替換的。Client面向接口(Figure和Manipulator)編程,當Client操作一條線段(Line)時,它必然要使用LineFigure和LineManipulator;而當Client操作一段文本(Text)時,也必然使用的是TextFigure和TextManipulator;ConcreteDocument依賴于Client的上下文才能確定,ConcreteFigure之間是不能相互替換的。抽象的Creator接口,不知道Client的上下文是什么,無從知曉該創建什么Product,于是就委托給子類來重寫。Factory Method強調的是Procuct與ConcreteProduct、Creator與ConcreteCreator之間的繼承關系,子類(ConcreteCreator)重寫父類(Creator)的創建產品接口(FactoryMethod()),從邏輯意義上講,可以看作“垂直擴展”。
而在實現一節中,GOF給出的MazeGame的例子:
圖中省略了創建的產品。雖然這里可以認為EnchantedMazeGame與BomedMazeGame可以互換,但是留意MazeGame的職責(它是對具體事物[迷宮游戲]的抽象,而不僅僅是一個創建各個Product的接口),以及他的CreateMaze方法(該方法是一個模板方法)。這里反復強調的是繼承父類、重寫創建Product的接口。
3.3 使用上:Abstract Factory的思想是聚合(Composition),而Factory Method的思想是繼承(Inheritance)
GOF在介紹完5個創建型模式后,有一個討論小結:“用一個系統創建的那些對象的類對系統進行參數化有兩種常用方式:一種是生成創建對象的類的子類,這對應于Factory Method模式”;“另一種對系統進行參數化的方法更多的依賴于對象復合:定義一個對象負責明確產品對象的類,并將它作為該系統的參數,這就是Abstract Factory、Builder和Prototype模式的關鍵特征。”
對于Factory Method,以Application/Document為例,Application(Creator)要使用Document(Product),把Document視為Application操作的一個“參數”,則特定的Document(ConcreteProduct)是由具體應用的Application(ConcreteCreator,Creator的子類)來創建的。
對于Abstract Factory,來看看GOF介紹的實現:
畫得比較亂,湊合著看,呵呵。重點是左上方的紅色注釋部分,其演繹的就是Abstract Factory的Composition思想:MazeGame聚合了一個MazeFactory。
另外兩個需要留意的地方:(1). 注意MazeFactory的職責:只是一組創建對象的接口;(2). MazeFactory包含了一組Factory Method。
4. 總結
Factory Method模式不是簡化版的Abstract Factory;相反,Abstract Factory模式中的Factory類,可以視為一個簡化版的Factory Method模式(例如本文開頭的兩個例子),其將Creator的職責單一化了,使之只具有創建的對象的職責。
Factory Method與Abstract Factory,在效果上都可用于連接平行的類層次(Factory Method不限于此),但是這兩個模式在思想上有著本質的區別:
- 對象職責上:Abstract Factory中的Factory,只具有創建對象(一個產品系列)的唯一職責;而Factory Method中的Creator,具有實際的邏輯和意義。
- 擴展上:Abstract Factory側重水平擴展,而Factory Method側重垂直擴展。
- 使用上:Abstract Factory的思想是聚合,而Factory Method的思想是繼承。
相關模式:Abstract Factory經常用工廠方法來實現。工廠方法通常在Template Method中被調用。
浙公網安備 33010602011771號