在前面兩章中,分別介紹了簡單工廠模式和工廠方法模式,我們知道簡單工廠模式的優點是去除了客戶端與具體產品的依賴,缺點是違反了“開放-關閉原則”;工廠方法模式克服了簡單工廠模式的缺點,將產品的創建工作放到具體的工廠類,每個工廠類負責生成一個產品。但是在實際應用中,一個工廠類只創建單個產品的情況很少,一般一個工廠類會負責創建一系列相關的產品,如果我們要設計這樣的系統,工廠方法模式顯然不能滿足應用的需求,本章要介紹的抽象工廠模式,可以很好地解決一系列產品創建的問題。
1. 何為抽象工廠模式
定義: 提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
抽象工廠模式的結構圖如圖1-1所示:
先對上面結構圖的幾個角色進行說明:
(1)AbstractFactory:抽象工廠接口,里面應該包含所有產品創建的抽象方法;
(2)ConcreteFactory1和ConcreteFactory2:具體的工廠,創建具有特定實現的產品對象;
(3)AbstractProductA和AbstractProductB:抽象產品,它們可能有多種不同的實現方式;
(4)ProductA1、ProductA2、ProductB1和ProductB2:具體的產品,是抽象產品的具體實現。
從結構圖中可以看到,抽象工廠方法最大的好處是能夠很方便的變換產品系列(例如id<AbstractFactory> factory =[ [ConcreteFactory1 alloc] init],只需要將ConcreteFactory1換成ConcreteFactory2,就可以創建ProductA2和ProductB2)。另外,抽象工廠方法讓具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中(例如id<AbstractProductA> product = [factory createProductA],客戶端根本不知道具體的類名是ProductA1還是ProductA2)。
但是,抽象工廠方法也是存在缺點的,比如說現在我們要增加一個新的產品,首先,我們需要增加三個類:AbstractProductC、ProductC1、ProductC2;另外,我們還需要更改三個類:AbstractFactory、ConcreteFactory1、ConcreteFactory2,這樣,很明顯是違背“開放-關閉原則”。這也是可以理解的,沒有任何一個設計模式是完美沒有瑕疵的,這就好比世界上沒有打不敗的武功一樣。我們可以做的就是在實際的需求中,盡可能的將變化點進行隔離,以達到變化發生的時候,對整個系統的影響最小,變化所帶來的變更和成本最低。
2. 代碼實現舉例
還是繼續簡單工廠模式和工廠方法模式的應用場景,這里將場景稍微改變一下:我們知道,繪制統計圖形的方案有多種,我們既可以使用OWC來繪制統計圖形,也可以使用HTML 5來繪制統計圖形,或者其他的一些第三方插件來進行繪圖,等等。這里我們用OWC和HTML 5繪制統計圖形來說明抽象工廠模式(注意:示例和場景只是為了說明設計模式的思想,并不是說實際開發中我們就會這么使用)。新應用場景使用抽象工廠模式實現的結構圖如圖2-1所示:
更具結構圖,一起來看看代碼實現方式:
(1)AbstractLine
@protocol AbstractLine <NSObject>
- (void)drawLine;
@end
(2)AbstractPie
@protocol AbstractPie <NSObject>
- (void)drawPie;
@end
(3)AbstractFactory
@protocol AbstractFactory <NSObject>
- (id<AbstractLine>)createLine;
- (id<AbstractPie>)createPie;
@end
(4)客戶端調用
id<AbstractFactory> factory = [[HTML5Factory alloc] init];
// id<AbstractFactory> factory = [[OWCFactory alloc] init];
id<AbstractLine> line = [factory createLine];
id<AbstractPie> pie = [factory createPie];
[line drawLine];
[pie drawPie];
3. 優缺點及其使用場景
從調用代碼我們可以看到抽象工廠的兩個優點:
能夠很方便的變換產品系列;
具體的創建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產品的具體類名也被具體工廠的實現分離,不會出現在客戶代碼中。
抽象工廠模式的缺點我們前面也分析了:在新加產品的需求下,違背開放-封閉原則。通過優缺點的比較,我們可以在如下場景下使用抽象工廠模式:功能模塊已經非常成熟,基本上不需要太多修改,但是有可能會替換掉實現這些功能模塊的類的那種情況。比如說數據庫鏈接,所有的JDBC功能模塊幾乎一樣,只不過種類有所不同,有些是SQL-Server,有些是Oracle,那么這時候用抽象工廠來實現,面對更換數據庫的情況,就比較方便了。
4. 幾種設計模式之間的對比
4.1 抽象工廠模式&建造者模式
抽象工廠模式和建造者模式都屬于創建型模式,它們在對象創建方面存在許多相似之處。但是,兩者也存在較大的區別,具體如下:
建造者模式 | 抽象工廠模式 |
---|---|
構建復雜對象 | 構建簡單或復雜對象 |
以多個步驟構建對象 | 以單一步驟構建對象 |
以多種方式構建對象 | 以單一方式構建對象 |
在構建過程的最后一步返回產品 | 立刻返回產品 |
專注一個特定產品 | 強調一套產品 |
4.2 抽象工廠模式&工廠方法模式
工廠方法模式是一種極端情況的抽象工廠模式,而抽象工廠模式可以看成是工廠方法模式的一種推廣。
工廠方法模式 | 抽象工廠模式 |
---|---|
只有一個抽象產品類 | 有多個抽象產品類 |
工廠類一般只有一個方法,創建一種產品 | 工廠類一般有多個方法,創建一些列產品 |