《軟件設計精要與模式》之Factory Method模式
Factory Method模式是應用最爲廣泛的設計模式,畢竟他負責了一系列對象的創建,而對象的創建正是面向對象編程中最爲繁瑣的行爲。《設計模式》一書寫到,“Factory Method模式使一個類的實例化延遲到子類。”準確的說,Factory Method模式是將創建對象實例的責任,轉移到了工廠類中,並利用抽象原理,將實例化行爲延遲到具體工廠類。
"嫁禍江東"之計
在面向對象的設計中,對象的管理是其核心所在,其中,對象的創建是對象管理的第一步。對象的創建非常簡單,在C#中,只需使用new操作符調用對象的構造方法即可,因此,管理對象最重要的是掌握創建對象的時機。
我們首先從對象的特徵來看。代表抽象意思的類型,如接口和抽象類,是不能創建對象實例的,這意味着我們要創建的對象都是與對象類型有關。也就是說,對象的創建工作必然涉及到設計中的實現細節,從而導致創建者與具體的被創建這之間耦合度增強。舉例來說,如果在一個項目中需要創建一些圖形對象,例如Circle,Square圖形,它們的結構如下圖所示。
這個類結構非常符合面向對象思想,他通過IShape接口將Square和Circle對象抽象出來。根據多態的原理,我們可以在程序中利用IShape接口代替具體的Square和Circle類,從而將具體的對象類型綁定留到運行時,但由於接口對象是不能創建的,一旦項目需求創建IShape類型的對象,就必然要針對具體的類對象進行創建操作。例如:
如果是開發一個圖形工具,諸如Square和Circle之類對象的創建工作必然非常頻繁。可以設想,在這個項目的各個摸塊中,如果大量充斥着如上代碼行,那麼導致的結果是各個模塊無法與Square具體對象解耦。當我們需要改變創建的對象爲Circle時,就必須修改所以調用new Square()操作的模塊。這無疑加大了修改的工作量,同時也導致了項目的不可擴展性,以及模塊的不可重用性;而對於圖形對象的抽象IShape接口來說,也成爲了不必要且失敗的設計。
我們的目的是要將這種變化給項目帶來的災難槍殺在搖籃之中,此時就需要利用“封裝變化”的原理。我們可以引入Factory Method模式對對象的創建行爲進行封裝。IShape類型的對象是工廠要生產的產品。在Factory Method模式中,工廠對象的結構應與產品的結構平行,並與之一一對應。既然具體的產品對象有兩種,相對應的具體工廠對象也應該是兩個,既SquareFactory和CircleFactory;同時,還應該爲這兩個工廠抽象出一個共同的工廠接口IShapeFactory。
實現代碼如下:
2{
3 public interface IShapeFactory
4 {
5 IShape CreateShape();
6 }
7}
8----------------------------------------------
9namespace DonOfDesign.FactoryPattern.GraphicLib
10{
11 public class SquareFactory : IShapeFactory
12 {
13 public IShape CreateShape()
14 {
15 return new Square();
16 }
17 }
18}
19----------------------------------------------
20namespace DonOfDesign.FactoryPattern.GraphicLib
21{
22 public class CircleFactory : IShapeFactory
23 {
24 public IShape CreateShape()
25 {
26 return new Circle();
27 }
28 }
29}
30
31
Factory Method模式實現了對象的創建封裝,將創建具體產品對象的代碼全部轉移到了各自的工廠對象中,並在CreateShape方法中實現,整個結構如下圖:
CreateShape方法的返回對象是IShape接口類型,這就有效地避免了工廠對象與具體產品對象的依賴,使得創建者與被創建者實現瞭解耦.
我想,真正的智者是不會被表象所迷惑的.事實上,Factory Method模式的引入,是一種"嫁禍江東"的伎倆,他依舊無法擺脫的職責轉交給工廠對象,然而在工廠類的結構中,依舊存在具體的工廠類對象.我門解出了模塊與具體圖象的依賴,卻又增加了對具體工廠對象的依賴,這會給我們的設計帶來何種益處?
讓我們從對象的創建頻率來分析.對於一個圖形分析工具而言,IShape對象的創建無疑是頻繁的,最大的可能行就在這個項目的各個模塊中都可能存在創建IShape對象的需要.工廠對象則不盡然,我門完全可以集中一個模塊中,初始化該工廠對象,並在需要IShape對象的時候,直接調用工廠實例CreateShape方法就可以達到目的.
因此,引入工廠對象並不是簡單的爲產品建立相應的工廠,而是要注意劃分各個模塊的職責,將工廠對象的創建放到合適的地方。最佳方案莫過於將創建工廠對象的職責集中起來,放到一個模塊中;而不是在需要創建產品時,才創建工廠對象。錯誤的例子是在創建產品時將工廠對象的創建與產品對象的創建放在一起,然後分佈在各個模塊中。這對於引入Factory Method模式來說,無非是畫蛇添足。
示例代碼下載:FactoryMetod.rar