實例代碼(JAVA):模式動機
在工廠方法模式中具體工廠負責生產具體的產品,每一個具體工廠對應一種具體產品,工廠方法也具有唯一性,一般情況下,一個具體工廠中只有一個工廠方法或者一組重載的工廠方法。但是有時候我們需要一個工廠可以提供多個產品對象,而不是單一的產品對象。
為了更清晰地理解工廠方法模式,需要先引入兩個概念:
•
產品等級結構:產品等級結構即產品的繼承結構,如一個抽象類是電視機,其子類有海爾電視機、海信電視機、TCL電視機,則抽象電視機與具體品牌的電視機之間構成了一個產品等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。
•
產品族:在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如海爾電器工廠生產的海爾電視機、海爾電冰箱,海爾電視機位於電視機產品等級結構中,海爾電冰箱位於電冰箱產品等級結構中。
產品族與產品等級結構示意圖:

當系統所提供的工廠所需生產的具體產品並不是一個簡單的對象,而是多個位於不同產品等級結構中屬於不同類型的具體產品時需要使用抽象工廠模式。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態。
抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構,一個工廠等級結構可以負責多個不同產品等級結構中的產品對象的創建 。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更為簡單、有效率。

模式定義
抽象工廠模式(Abstract Factory Pattern):提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬於對象創建型模式。
模式結構

抽象工廠模式包含如下角色:
•
AbstractFactory:抽象工廠
•
ConcreteFactory:具體工廠
•
AbstractProduct:抽象產品
•
Product:具體產品
模式分析

抽象工廠類的典型代碼如下:
1 public abstract class AbstractFactory 2 { 3 public abstract AbstractProductA createProductA(); 4 public abstract AbstractProductB createProductB(); 5 }
具體工廠類的典型代碼如下:
1 public class ConcreteFactory1 extends AbstractFactory 2 { 3 public AbstractProductA createProductA() 4 { 5 return new ConcreteProductA1(); 6 } 7 public AbstractProductB createProductB() 8 { 9 return new ConcreteProductB1(); 10 } 11 }
模式實例與解析
實例一:電器工廠
• 一個電器工廠可以產生多種類型的電器,如海爾工廠可以生產海爾電視機、海爾空調等,TCL工廠可以生產TCL電視機、TCL空調等,相同品牌的電器構成一個產品族,而相同類型的電器構成了一個產品等級結構,現使用抽象工廠模式模擬該場景。

實例代碼(JAVA):
1 //抽象產品 Television 2 public interface Television 3 { 4 public void play(); 5 } 6 7 //具體產品 HaierTelevision 8 public class HaierTelevision implements Television 9 { 10 public void play() 11 { 12 System.out.println("海爾電視機播放中......"); 13 } 14 } 15 16 //具體產品 TCLTelevision 17 public class TCLTelevision implements Television 18 { 19 public void play() 20 { 21 System.out.println("TCL電視機播放中......"); 22 } 23 } 24 25 //抽象產品 AirConditioner 26 public interface AirConditioner 27 { 28 public void changeTemperature(); 29 } 30 31 //具體產品 HaierAirConditioner 32 public class HaierAirConditioner implements AirConditioner 33 { 34 public void changeTemperature() 35 { 36 System.out.println("海爾空調溫度改變中......"); 37 } 38 } 39 40 //具體產品 TCLAirConditioner 41 public class TCLAirConditioner implements AirConditioner 42 { 43 public void changeTemperature() 44 { 45 System.out.println("TCL空調溫度改變中......"); 46 } 47 } 48 49 //抽象工廠 EFactory 50 public interface EFactory 51 { 52 public Television produceTelevision(); 53 public AirConditioner produceAirConditioner(); 54 } 55 56 //具體工廠 HaierFactory 57 public class HaierFactory implements EFactory 58 { 59 public Television produceTelevision() 60 { 61 return new HaierTelevision(); 62 } 63 64 public AirConditioner produceAirConditioner() 65 { 66 return new HaierAirConditioner(); 67 } 68 } 69 70 //具體工廠 TCLFactory 71 public class TCLFactory implements EFactory 72 { 73 public Television produceTelevision() 74 { 75 return new TCLTelevision(); 76 } 77 78 public AirConditioner produceAirConditioner() 79 { 80 return new TCLAirConditioner(); 81 } 82 } 83 84 //配置文件 config.xml 85 <?xml version="1.0"?> 86 <config> 87 <className>HaierFactory</className> 88 </config> 89 90 //通過反射獲得具體工廠的實例 XMLUtil 91 import javax.xml.parsers.*; 92 import org.w3c.dom.*; 93 import org.xml.sax.SAXException; 94 import java.io.*; 95 public class XMLUtil 96 { 97 //該方法用於從XML配置文件中提取具體類類名,並返回一個實例對象 98 public static Object getBean() 99 { 100 try 101 { 102 //創建文檔對象 103 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); 104 DocumentBuilder builder = dFactory.newDocumentBuilder(); 105 Document doc; 106 doc = builder.parse(new File("config.xml")); 107 108 //獲取包含類名的文本節點 109 NodeList nl = doc.getElementsByTagName("className"); 110 Node classNode=nl.item(0).getFirstChild(); 111 String cName=classNode.getNodeValue(); 112 113 //通過類名生成實例對象並將其返回 114 Class c=Class.forName(cName); 115 Object obj=c.newInstance(); 116 return obj; 117 } 118 catch(Exception e) 119 { 120 e.printStackTrace(); 121 return null; 122 } 123 } 124 } 125 126 //客戶端類 Client 127 public class Client 128 { 129 public static void main(String args[]) 130 { 131 try 132 { 133 EFactory factory; 134 Television tv; 135 AirConditioner ac; 136 factory=(EFactory)XMLUtil.getBean(); 137 tv=factory.produceTelevision(); 138 tv.play(); 139 ac=factory.produceAirConditioner(); 140 ac.changeTemperature(); 141 } 142 catch(Exception e) 143 { 144 System.out.println(e.getMessage()); 145 } 146 } 147 }
實例代碼(C++):
1 // 抽象工廠模式 2 #include <iostream> 3 using namespace std; 4 5 //抽象產品類 Television 6 class Television 7 { 8 public: 9 virtual void play() = 0; 10 }; 11 12 //具體產品類 HaierTelevision 13 class HaierTelevision:public Television 14 { 15 public: 16 void play() override 17 { 18 cout << "海爾電視播放中..." << endl; 19 } 20 }; 21 22 //具體產品類 TCLTelevision 23 class TCLTelevision : public Television 24 { 25 public: 26 void play() override 27 { 28 cout << "TCL電視播放中..." << endl; 29 } 30 }; 31 32 //抽象產品 AirConditioner 33 class AirConditioner 34 { 35 public: 36 virtual void changeTemperature() = 0; 37 }; 38 39 //具體產品 HaierAirConditioner 40 class HaierAirConditioner : public AirConditioner 41 { 42 public: 43 void changeTemperature() override 44 { 45 cout << "海爾空調溫度改變中..." << endl; 46 } 47 }; 48 49 //具體產品 TCLAirConditioner 50 class TCLAirConditioner : public AirConditioner 51 { 52 public: 53 void changeTemperature() override 54 { 55 cout << "TCL空調溫度改變中..." << endl; 56 } 57 }; 58 59 //抽象工廠 EFactory 60 class EFactory 61 { 62 public: 63 virtual Television* productTelevision() = 0; 64 virtual AirConditioner* productAirConditioner() = 0; 65 }; 66 67 //具體工廠 HaierFactory 68 class HaierFactory : public EFactory 69 { 70 public: 71 Television* productTelevision() override 72 { 73 return new HaierTelevision(); 74 } 75 76 AirConditioner* productAirConditioner() override 77 { 78 return new HaierAirConditioner(); 79 } 80 }; 81 82 //具體工廠 TCLFactory 83 class TCLFactory : public EFactory 84 { 85 public: 86 Television* productTelevision() override 87 { 88 return new TCLTelevision(); 89 } 90 91 AirConditioner* productAirConditioner() override 92 { 93 return new TCLAirConditioner(); 94 } 95 }; 96 97 //客戶端 98 int main() 99 { 100 EFactory* factory; 101 Television* tv; 102 AirConditioner* ac; 103 factory = new HaierFactory(); 104 tv = factory->productTelevision(); 105 tv->play(); 106 ac = factory->productAirConditioner(); 107 ac->changeTemperature(); 108 109 factory = new TCLFactory(); 110 tv = factory->productTelevision(); 111 tv->play(); 112 ac = factory->productAirConditioner(); 113 ac->changeTemperature(); 114 return 0; 115 }
運行結果:

實例二:數據庫操作工廠
• 某系統為了改進數據庫操作的性能,自定義數據庫連接對象Connection和語句對象Statement,可針對不同類型的數據庫提供不同的連接對象和語句對象,如提供Oracle或SQL Server專用連接類和語句類,而且用戶可以通過配置文件等方式根據實際需要動態更換系統數據庫。使用抽象工廠模式設計該系統。

模式優缺點
優點
• 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什么被創建。由於這種隔離,更換一個具體工廠就變得相對容易。所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行為。另外,應用抽象工廠模式可以實現高內聚低耦合的設計目的,因此抽象工廠模式得到了廣泛的應用。
• 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。這對一些需要根據當前環境來決定其行為的軟件系統來說,是一種非常實用的設計模式。
• 增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。
缺點
• 在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品,這是因為在抽象工廠角色中規定了所有可能被創建的產品集合,要支持新種類的產品就意味着要對該接口進行擴展,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。
• 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)
模式適用環境
在以下情況下可以使用抽象工廠模式:
• 一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節,這對於所有類型的工廠模式都是重要的。
• 系統中有多於一個的產品族,而每次只使用其中某一產品族。
• 屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。
• 系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於具體實現。
模式應用
(1) Java SE AWT(抽象窗口工具包)
• 在Java語言的AWT(抽象窗口工具包)中就使用了抽象工廠模式,它使用抽象工廠模式來實現在不同的操作系統中應用程序呈現與所在操作系統一致的外觀界面。
(2) 在很多軟件系統中需要更換界面主題,要求界面中的按鈕、文本框、背景色等一起發生改變時,可以使用抽象工廠模式進行設計。
模式擴展
“開閉原則”的傾斜性
• “開閉原則”要求系統對擴展開放,對修改封閉,通過擴展達到增強其功能的目的。對於涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:
• (1) 增加產品族:對於增加新的產品族,工廠方法模式很好的支持了“開閉原則”,對於新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有代碼無須做任何修改。
• (2) 增加新的產品等級結構:對於增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,不能很好地支持“開閉原則”。
• 抽象工廠模式的這種性質稱為“開閉原則”的傾斜性,抽象工廠模式以一種傾斜的方式支持增加新的產品,它為新產品族的增加提供方便,但不能為新的產品等級結構的增加提供這樣的方便。
工廠模式的退化
• 當抽象工廠模式中每一個具體工廠類只創建一個產品對象,也就是只存在一個產品等級結構時,抽象工廠模式退化成工廠方法模式;當工廠方法模式中抽象工廠與具體工廠合並,提供一個統一的工廠來創建產品對象,並將創建對象的工廠方法設計為靜態方法時,工廠方法模式退化成簡單工廠模式。