抽象工廠

以統一塑模語言中的類別圖來表示抽象工廠
抽象工廠模式（Abstract factory pattern）是一種軟件開發設計模式。抽象工廠模式提供了一種方式，可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中，客戶端程序需要創建抽象工廠的具體實現，然後使用抽象工廠作為接口來創建這一主題的具體對象。客戶端程序不需要知道（或關心）它從這些內部的工廠方法中獲得對象的具體類型，因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。
舉個例子來說，比如一個抽象工廠類叫做DocumentCreator（文檔創建器），此類提供創建若干種產品的接口，包括createLetter()（創建信件）和createResume()（創建簡歷）。其中，createLetter()返回一個Letter（信件），createResume()返回一個Resume（簡歷）。系統中還有一些DocumentCreator的具體實現類，包括FancyDocumentCreator和ModernDocumentCreator。這兩個類對DocumentCreator的兩個方法分別有不同的實現，用來創建不同的“信件”和“簡歷”（用FancyDocumentCreator的實例可以創建FancyLetter和FancyResume，用ModernDocumentCreator的實例可以創建ModernLetter和ModernResume）。這些具體的“信件”和“簡歷”類均繼承自抽象類，即Letter和Resume類。客戶端需要創建“信件”或“簡歷”時，先要得到一個合適的DocumentCreator實例，然後調用它的方法。一個工廠中創建的每個對象都是同一個主題的（“fancy”或者“modern”）。客戶端程序只需要知道得到的對象是“信件”或者“簡歷”，而不需要知道具體的主題，因此客戶端程序從抽象工廠DocumentCreator中得到了Letter或Resume類的引用，而不是具體類的對象引用。
“工廠”是創建產品（對象）的地方，其目的是將產品的創建與產品的使用分離。抽象工廠模式的目的，是將若干抽象產品的接口與不同主題產品的具體實現分離開。這樣就能在增加新的具體工廠的時候，不用修改引用抽象工廠的客戶端代碼。
使用抽象工廠模式，能夠在具體工廠變化的時候，不用修改使用工廠的客戶端代碼，甚至是在運行時。然而，使用這種模式或者相似的設計模式，可能給編寫代碼帶來不必要的複雜性和額外的工作。正確使用設計模式能夠抵消這樣的“額外工作”。

**定義**
抽象工廠模式的實質是“提供接口，創建一系列相關或獨立的對象，而不指定這些對象的具體類。”

**使用**
具體的工廠決定了創建對象的具體類型，而且工廠就是對象實際創建的地方（比如在C++中，用“new”操作符創建對象）。然而，抽象工廠只返回一個指向創建的對象的抽象引用（或指針）。
這樣，客戶端程序調用抽象工廠引用的方法，由具體工廠完成對象創建，然後客戶端程序得到的是抽象產品的引用。如此使客戶端代碼與對象的創建分離開來。
因為工廠僅僅返回一個抽象產品的引用（或指針），所以客戶端程序不知道（也不會牽絆於）工廠創建對象的具體類型。然而，工廠知道具體對象的類型；例如，工廠可能從配置文件中讀取某種類型。這時，客戶端沒有必要指定具體類型，因為已經在配置文件中指定了。通常，這意味著：- 客戶端代碼不知道任何具體類型，也就沒必要引入任何相關的頭文件或類定義。客戶端代碼僅僅處理抽象類型。工廠確實創建了具體類型的對象，但是客戶端代碼僅使用這些對象的抽象接口來訪問它們。- 如果要增加一個具體類型，只需要修改客戶端代碼使用另一個工廠即可，而且這個修改通常只是一個文件中的一行代碼。不同的工廠創建不同的具體類型的對象，但是和以前一樣返回一個抽象類型的引用（或指針），因此客戶端代碼的其他部分不需要任何改動。這樣比修改客戶端代碼創建新類型的對象簡單多了。如果是後者的話，需要修改代碼中每一個創建這種對象的地方（而且需要注意的是，這些地方都知道對象的具體類型，而且需要引入具體類型的頭文件或類定義）。如果所有的工廠對象都存儲在全局的單例對象中，所有的客戶端代碼到這個單例中訪問需要的工廠，那麼，更換工廠就非常簡單了，僅僅需要更改這個單例對象即可。

**結構**

**LePUS3圖**
File:Abstract Factory in LePUS3.png

**類圖**
File:Abstract factory.svg
GuiFactory接口中的createButton方法返回Button類型的對象。返回Button的哪種實現依賴於使用GuiFactory的哪種實現。
需要注意的是，為了簡潔起見，以上類圖僅僅展示了創建一個類型對象的工廠。而在抽象工廠模式中，通常一個工廠能夠創建若干種不同類型的對象。

**代碼舉例**
假設我們有兩種產品接口 Button 和 Border ，每一種產品都支持多種系列，比如 Mac 系列和 Windows 系列。這樣每個系列的產品分別是 MacButton, WinButton, MacBorder, WinBorder 。為了可以在運行時刻創建一個系列的產品族，我們可以為每個系列的產品族建立一個工廠 MacFactory 和 WinFactory 。每個工廠都有兩個方法 CreateButton 和 CreateBorder 並返回對應的產品，可以將這兩個方法抽象成一個接口 AbstractFactory 。這樣在運行時刻我們可以選擇創建需要的產品系列。

**C++**
我們的產品結構是這樣的
class Button; // Abstract Class
class MacButton: public Button {};
class WinButton: public Button {};
class Border; // Abstract Class
class MacBorder: public Border {};
class WinBorder: public Border {};
對應的工廠是這樣的
class AbstractFactory {
public:
virtual Button* CreateButton() =0;
virtual Border* CreateBorder() =0;
};
class MacFactory: public AbstractFactory {
public:
MacButton* CreateButton() { return new MacButton; }
MacBorder* CreateBorder() { return new MacBorder; }
};
class WinFactory: public AbstractFactory {
public:
WinButton* CreateButton() { return new WinButton; }
WinBorder* CreateBorder() { return new WinBorder; }
};
那麼客戶可以根據需要選擇 Mac 風格或者 Win 風格來創建 Button 或 Border
AbstractFactory* fac;
switch (style) {
case MAC:
fac = new MacFactory;
break;
case WIN:
fac = new WinFactory;
break;
}
Button* button = fac->CreateButton();
Border* border = fac->CreateBorder();

**PHP**
那麼客戶可以根據需要選擇 Mac 風格或者 Win 風格的 Button 或 Border 來創建
CreateButton();
$factory->CreateBorder();
?>

**Java**
 使用上面的例子
public interface Button {}
public interface Border {}- 實現抽象類
public class MacButton implements Button {}
public class MacBorder implements Border {}
public class WinButton implements Button {}
public class WinBorder implements Border {}- 接著實現工廠
public class MacFactory {
	public static Button createButton() {
	    return new MacButton();
	}
	public static Border createBorder() {
	    return new MacBorder();
	}
}
public class WinFactory {
	public static Button createButton() {
	    return new WinButton();
	}
	public static Border createBorder() {
	    return new WinBorder();
	}
}

**適用性**
在以下情況可以考慮使用抽象工廠模式：- 一個系統要獨立於它的產品的創建、組合和表示時。- 一個系統要由多個產品系列中的一個來配置時。- 需要強調一系列相關的產品對象的設計以便進行聯合使用時。- 提供一個產品類庫，而只想顯示它們的接口而不是實現時。

**優點**
# 具體產品從客戶代碼中被分離出來
# 容易改變產品的系列
# 將一個系列的產品族統一到一起創建

**缺點**
# 在產品族中擴展新的產品是很困難的，它需要修改抽象工廠的接口

**參考文獻**

**參見**
 工廠方法模式- 單例模式- 設計模式 (計算機)