在前面兩章中,分別介紹了簡單工廠模式和工廠方法模式,我們知道簡單工廠模式的優(yōu)點是去除了客戶端與具體產(chǎn)品的依賴,缺點是違反了“開放-關(guān)閉原則”;工廠方法模式克服了簡單工廠模式的缺點,將產(chǎn)品的創(chuàng)建工作放到具體的工廠類,每個工廠類負責(zé)生成一個產(chǎn)品。但是在實際應(yīng)用中,一個工廠類只創(chuàng)建單個產(chǎn)品的情況很少,一般一個工廠類會負責(zé)創(chuàng)建一系列相關(guān)的產(chǎn)品,如果我們要設(shè)計這樣的系統(tǒng),工廠方法模式顯然不能滿足應(yīng)用的需求,本章要介紹的抽象工廠模式,可以很好地解決一系列產(chǎn)品創(chuàng)建的問題。
1. 何為抽象工廠模式
定義: 提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定它們具體的類。
抽象工廠模式的結(jié)構(gòu)圖如圖1-1所示:

先對上面結(jié)構(gòu)圖的幾個角色進行說明:
(1)AbstractFactory:抽象工廠接口,里面應(yīng)該包含所有產(chǎn)品創(chuàng)建的抽象方法;
(2)ConcreteFactory1和ConcreteFactory2:具體的工廠,創(chuàng)建具有特定實現(xiàn)的產(chǎn)品對象;
(3)AbstractProductA和AbstractProductB:抽象產(chǎn)品,它們可能有多種不同的實現(xiàn)方式;
(4)ProductA1、ProductA2、ProductB1和ProductB2:具體的產(chǎn)品,是抽象產(chǎn)品的具體實現(xiàn)。
從結(jié)構(gòu)圖中可以看到,抽象工廠方法最大的好處是能夠很方便的變換產(chǎn)品系列(例如id<AbstractFactory> factory =[ [ConcreteFactory1 alloc] init],只需要將ConcreteFactory1換成ConcreteFactory2,就可以創(chuàng)建ProductA2和ProductB2)。另外,抽象工廠方法讓具體的創(chuàng)建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產(chǎn)品的具體類名也被具體工廠的實現(xiàn)分離,不會出現(xiàn)在客戶代碼中(例如id<AbstractProductA> product = [factory createProductA],客戶端根本不知道具體的類名是ProductA1還是ProductA2)。
但是,抽象工廠方法也是存在缺點的,比如說現(xiàn)在我們要增加一個新的產(chǎn)品,首先,我們需要增加三個類:AbstractProductC、ProductC1、ProductC2;另外,我們還需要更改三個類:AbstractFactory、ConcreteFactory1、ConcreteFactory2,這樣,很明顯是違背“開放-關(guān)閉原則”。這也是可以理解的,沒有任何一個設(shè)計模式是完美沒有瑕疵的,這就好比世界上沒有打不敗的武功一樣。我們可以做的就是在實際的需求中,盡可能的將變化點進行隔離,以達到變化發(fā)生的時候,對整個系統(tǒng)的影響最小,變化所帶來的變更和成本最低。
2. 代碼實現(xiàn)舉例
還是繼續(xù)簡單工廠模式和工廠方法模式的應(yīng)用場景,這里將場景稍微改變一下:我們知道,繪制統(tǒng)計圖形的方案有多種,我們既可以使用OWC來繪制統(tǒng)計圖形,也可以使用HTML 5來繪制統(tǒng)計圖形,或者其他的一些第三方插件來進行繪圖,等等。這里我們用OWC和HTML 5繪制統(tǒng)計圖形來說明抽象工廠模式(注意:示例和場景只是為了說明設(shè)計模式的思想,并不是說實際開發(fā)中我們就會這么使用)。新應(yīng)用場景使用抽象工廠模式實現(xiàn)的結(jié)構(gòu)圖如圖2-1所示:

更具結(jié)構(gòu)圖,一起來看看代碼實現(xiàn)方式:
(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)客戶端調(diào)用
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. 優(yōu)缺點及其使用場景
從調(diào)用代碼我們可以看到抽象工廠的兩個優(yōu)點:
能夠很方便的變換產(chǎn)品系列;
具體的創(chuàng)建實例過程與客戶端分離,客戶端是通過它們的抽象接口操作實例,產(chǎn)品的具體類名也被具體工廠的實現(xiàn)分離,不會出現(xiàn)在客戶代碼中。
抽象工廠模式的缺點我們前面也分析了:在新加產(chǎn)品的需求下,違背開放-封閉原則。通過優(yōu)缺點的比較,我們可以在如下場景下使用抽象工廠模式:功能模塊已經(jīng)非常成熟,基本上不需要太多修改,但是有可能會替換掉實現(xiàn)這些功能模塊的類的那種情況。比如說數(shù)據(jù)庫鏈接,所有的JDBC功能模塊幾乎一樣,只不過種類有所不同,有些是SQL-Server,有些是Oracle,那么這時候用抽象工廠來實現(xiàn),面對更換數(shù)據(jù)庫的情況,就比較方便了。
4. 幾種設(shè)計模式之間的對比
4.1 抽象工廠模式&建造者模式
抽象工廠模式和建造者模式都屬于創(chuàng)建型模式,它們在對象創(chuàng)建方面存在許多相似之處。但是,兩者也存在較大的區(qū)別,具體如下:
| 建造者模式 | 抽象工廠模式 |
|---|---|
| 構(gòu)建復(fù)雜對象 | 構(gòu)建簡單或復(fù)雜對象 |
| 以多個步驟構(gòu)建對象 | 以單一步驟構(gòu)建對象 |
| 以多種方式構(gòu)建對象 | 以單一方式構(gòu)建對象 |
| 在構(gòu)建過程的最后一步返回產(chǎn)品 | 立刻返回產(chǎn)品 |
| 專注一個特定產(chǎn)品 | 強調(diào)一套產(chǎn)品 |
4.2 抽象工廠模式&工廠方法模式
工廠方法模式是一種極端情況的抽象工廠模式,而抽象工廠模式可以看成是工廠方法模式的一種推廣。
| 工廠方法模式 | 抽象工廠模式 |
|---|---|
| 只有一個抽象產(chǎn)品類 | 有多個抽象產(chǎn)品類 |
| 工廠類一般只有一個方法,創(chuàng)建一種產(chǎn)品 | 工廠類一般有多個方法,創(chuàng)建一些列產(chǎn)品 |