抽象工廠模式

抽象工廠模式

抽象工廠模式是所有形態的工廠模式中最為抽象和最具一般性的一種形態。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產品的具體的情況下,創建多個產品族中的產品對象。根據里氏替換原則,任何接受父類型的地方,都應當能夠接受子類型。因此,實際上系統所需要的,僅僅是類型與這些抽象產品角色相同的一些實例,而不是這些抽象產品的實例。換言之,也就是這些抽象產品的具體子類的實例。工廠類負責創建抽象產品的具體子類的實例。

基本介紹

  • 中文名:抽象工廠模式
  • 外文名:Abstract Factory
  • 是指:當有多個抽象角色
  • 根據:LSP原則
  • 創建:多個產品族中的產品對象
定義,簡介,產品族,使用情況,使用,代碼舉例,C++,PHP,Java,適用性,優點,缺點,

定義

為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。

簡介

當每個抽象產品都有多於一個的具體子類的時候,工廠角色怎么知道實例化哪一個子類呢?比如每個抽象產品角色都有兩個具體產品。抽象工廠模式提供兩個具體工廠角色,分別對應於這兩個具體產品角色,每一個具體工廠角色只負責某一個產品角色的實例化。每一個具體工廠類只負責創建抽象產品的某一個具體子類的實例。
每一個模式都是針對一定問題的解決方案,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式針對的是多個產品等級結構。

產品族

是指位於不同產品等級結構中,功能相關聯的產品組成的家族。一般是位於不同的等級結構中的相同位置上。顯然,每一個產品族中含有產品的數目,與產品等級結構的數目是相等的,形成一個二維的坐標系,水平坐標是產品等級結構,縱坐標是產品族。叫做相圖。
當有多個不同的等級結構的產品時,如果使用工廠方法模式就勢必要使用多個獨立的工廠等級結構來對付這些產品的等級結構。如果這些產品等級結構是平行的,會導致多個平行的工廠等級結構。
抽象工廠模式使用同一個 工廠等級結構負責這些不同產品等級結構產品對象的創建。
對於每一個產品族,都有一個具體工廠。而每一個具體工廠創建屬於同一個產品族,但是分屬於不同等級結構的產品。
通過引進抽象工廠模式,可以處理具有相同(或者相似)等級結構的多個產品族中的產品對象的創建問題。
由於每個具體工廠角色都需要負責兩個不同等級結構的產品對象的創建,因此每個工廠角色都需要提供兩個工廠方法,分別用於創建兩個等級結構的產品。既然每個具體工廠角色都需要實現這兩個工廠方法,所以具有一般性,不妨抽象出來,移動到抽象工廠角色中加以聲明。

使用情況

1.系統不依賴於產品類實例如何被創建,組合和表達的細節。
2.系統的產品有多於一個的產品族,而系統只消費其中某一族的產品(抽象工廠模式的原始用意Unix&Windows)
Button--->UnixButton/WinButton
Text----->UnixText/WinText
Unix產品族和Windows產品族,不會同時使用。
Factory--->UnixFactory/WinFactory
3.同屬於同一個產品族是在一起使用的。這一約束必須在系統的設計中體現出來。
4.系統提供一個產品類的庫,所有產品以同樣的接口出現,從而使客戶端不依賴於實現。

使用

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

代碼舉例

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

C++

我們的產品結構是這樣的
class Button; // Abstract Classclass MacButton: public Button {};class WinButton: public Button {};class Border; // Abstract Classclass 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

<?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();

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();    }}

適用性

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

優點

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

缺點

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

相關詞條

熱門詞條

聯絡我們