抽象工廠模式(英語:Abstract factory pattern)是一種軟件開發設計模式。抽象工廠模式提供了一種方式,可以將一組具有同一主題的單獨的工廠封裝起來。在正常使用中,客戶端程序需要創建抽象工廠的具體實現,然後使用抽象工廠作為接口來創建這一主題的具體對象。客戶端程序不需要知道(或關心)它從這些內部的工廠方法中獲得對象的具體類型,因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。

統一塑模語言中的類別圖來表示抽象工廠

舉個例子來說,比如一個抽象工廠類叫做DocumentCreator(文檔創建器),此類提供創建若干種產品的接口,包括createLetter()(創建信件)和createResume()(創建簡歷)。其中,createLetter()返回一個Letter(信件),createResume()返回一個Resume(簡歷)。系統中還有一些DocumentCreator的具體實現類,包括FancyDocumentCreatorModernDocumentCreator。這兩個類對DocumentCreator的兩個方法分別有不同的實現,用來創建不同的「信件」和「簡歷」(用FancyDocumentCreator的實例可以創建FancyLetterFancyResume,用ModernDocumentCreator的實例可以創建ModernLetterModernResume)。這些具體的「信件」和「簡歷」類均繼承自抽象類,即LetterResume類。客戶端需要創建「信件」或「簡歷」時,先要得到一個合適的DocumentCreator實例,然後調用它的方法。一個工廠中創建的每個對象都是同一個主題的(「fancy」或者「modern」)。客戶端程序只需要知道得到的對象是「信件」或者「簡歷」,而不需要知道具體的主題,因此客戶端程序從抽象工廠DocumentCreator中得到了LetterResume類的引用,而不是具體類的對象引用。

「工廠」是創建產品(對象)的地方,其目的是將產品的創建與產品的使用分離。抽象工廠模式的目的,是將若干抽象產品的接口與不同主題產品的具體實現分離開。這樣就能在增加新的具體工廠的時候,不用修改引用抽象工廠的客戶端代碼。

使用抽象工廠模式,能夠在具體工廠變化的時候,不用修改使用工廠的客戶端代碼,甚至是在運行時。然而,使用這種模式或者相似的設計模式,可能給編寫代碼帶來不必要的複雜性和額外的工作。正確使用設計模式能夠抵消這樣的「額外工作」。

定義

編輯

抽象工廠模式的實質是「提供接口,創建一系列相關或獨立的對象,而不指定這些對象的具體類。」[1]

使用

編輯

具體的工廠決定了創建對象的具體類型,而且工廠就是對象實際創建的地方(比如在C++中,用「new」操作符創建對象)。然而,抽象工廠只返回一個指向創建的對象的抽象引用(或指針)。

這樣,客戶端程序調用抽象工廠引用的方法,由具體工廠完成對象創建,然後客戶端程序得到的是抽象產品的引用。如此使客戶端代碼與對象的創建分離開來。[2]

因為工廠僅僅返回一個抽象產品的引用(或指針),所以客戶端程序不知道(也不會牽絆於)工廠創建對象的具體類型。然而,工廠知道具體對象的類型;例如,工廠可能從配置文件中讀取某種類型。這時,客戶端沒有必要指定具體類型,因為已經在配置文件中指定了。通常,這意味着:

  • 客戶端代碼不知道任何具體類型,也就沒必要引入任何相關的頭文件或類定義。客戶端代碼僅僅處理抽象類型。工廠確實創建了具體類型的對象,但是客戶端代碼僅使用這些對象的抽象接口來訪問它們。[3]
  • 如果要增加一個具體類型,只需要修改客戶端代碼使用另一個工廠即可,而且這個修改通常只是一個文件中的一行代碼。不同的工廠創建不同的具體類型的對象,但是和以前一樣返回一個抽象類型的引用(或指針),因此客戶端代碼的其他部分不需要任何改動。這樣比修改客戶端代碼創建新類型的對象簡單多了。如果是後者的話,需要修改代碼中每一個創建這種對象的地方(而且需要注意的是,這些地方都知道對象的具體類型,而且需要引入具體類型的頭文件或類定義)。如果所有的工廠對象都存儲在全局的單例對象中,所有的客戶端代碼到這個單例中訪問需要的工廠,那麼,更換工廠就非常簡單了,僅僅需要更改這個單例對象即可。[3]

結構

編輯

LePUS3圖

編輯

 

 

GuiFactory接口中的createButton方法返回Button類型的對象。返回Button的哪種實現依賴於使用GuiFactory的哪種實現。

需要注意的是,為了簡潔起見,以上類圖僅僅展示了創建一個類型對象的工廠。而在抽象工廠模式中,通常一個工廠能夠創建若干種不同類型的對象。

代碼舉例

編輯

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

我們的產品結構是這樣的

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
/***************************************************************************
 *              AbstractFactory.php
 *              -------------------
 *   Time  :    2006-11-11
 *   Coder :    rollenc(http://www.rollenc.com)
 *              syre(http://syre.blogbus.com)
 ***************************************************************************/
abstract class AbstractFactory {
	abstract public function CreateButton();
	abstract public function CreateBorder();
}

class MacFactory extends AbstractFactory{
	public function CreateButton()
	{
		return new MacButton();
	}
	public function CreateBorder()
	{
		return new MacBorder();
	}
}
class WinFactory extends AbstractFactory{
	public function CreateButton()
	{
		return new WinButton();
	}
	public function CreateBorder()
	{
		return new WinBorder();
	}
}
class Button{}
class Border{}

class MacButton extends Button{
	function __construct()
	{
		echo 'MacButton is created' . "\n";
	}
}
class MacBorder extends Border{
	function __construct()
	{
		echo 'MacBorder is created' . "\n";
	}
}


class WinButton extends Button{
	function __construct()
	{
		echo 'WinButton is created' . "\n";
	}
}
class WinBorder extends Border{
	function __construct()
	{
		echo 'WinBorder is created' . "\n";
	}
}
?>

那麼客戶可以根據需要選擇 Mac 風格或者 Win 風格的 Button 或 Border 來創建

<?
$type = 'Mac'; //value by user.
if(!in_array($type, array('Win','Mac')))
    die('Type Error');
$factoryClass = $type.'Factory';
$factory=new $factoryClass;
$factory->CreateButton();
$factory->CreateBorder();
?>
  • 使用上面的例子
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();
	}
}

適用性

編輯

在以下情況可以考慮使用抽象工廠模式:

  • 一個系統要獨立於它的產品的創建、組合和表示時。
  • 一個系統要由多個產品系列中的一個來配置時。
  • 需要強調一系列相關的產品對象的設計以便進行聯合使用時。
  • 提供一個產品類庫,而只想顯示它們的接口而不是實現時。

優點

編輯
  1. 具體產品從客戶代碼中被分離出來
  2. 容易改變產品的系列
  3. 將一個系列的產品族統一到一起創建

缺點

編輯
  1. 在產品族中擴展新的產品是很困難的,它需要修改抽象工廠的接口

參考文獻

編輯
  1. ^ [Erich]; Richard Helm, Ralph Johnson, John M. Vlissides. Design Patterns: Abstract Factory (HTML). informIT. 2009-10-23 [2012-05-16]. (原始內容存檔於2021-03-22) (英語). Object Creational: Abstract Factory: Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.  請檢查|author-link1=值 (幫助)
  2. ^ [David]. Object Design for the Perplexed (HTML). The Code Project. 2009-10-23 [2012-05-16]. (原始內容存檔於2011-02-21) (英語). The factory insulates the client from changes to the product or how it is created, and it can provide this insulation across objects derived from very different abstract interfaces.  請檢查|author-link1=值 (幫助)
  3. ^ 3.0 3.1 Abstract Factory: Implementation (HTML). OODesign.com. [2012-05-16]. (原始內容存檔於2021-03-10) (英語). 

參見

編輯