2014-10-10 185 views
4

我一直在閱讀關於抽象工廠模式,但即使我理解它,我也不知道何時使用它。模式的定義表明它用於抽象對象實例化,但它對我來說不是很清楚,爲什麼我需要這樣做,爲什麼我應該關心對象實例化以及爲什麼(或何時)對抽象它。什麼時候應該使用抽象工廠模式

+0

你確定你想要'抽象工廠'模式,而不僅僅是標準'工廠模式?他們是兩個不同但相關的東西。 – 2014-10-10 12:00:14

回答

1

讓我們假裝你正在編寫一個需要與數據庫交談的應用程序。您可以擁有一個類似工廠的Database類,以使用Database.CreateCommand方法創建命令。如果您需要使用不同的數據庫引擎,則每個引擎需要不同的Database實現。

您可能在運行時不知道您將需要哪個命令工廠,因此您創建了一個DatabaseManager類,該類具有返回特定類型的DataBase的DatabaseManager.GetDatabase(databaseType)函數。 DatabaseType可能來自配置文件,因此可以輕鬆更改。

在這個例子中,每個Database將是一個普通的工廠,而DatabaseManager將是一個抽象工廠。這是一家創建其他工廠的工廠。

所以,你可以在本質上做這樣的事情:

Dim sqlCommand as ICommand = DatabaseManager.GetDatabase("MsSQL").CreateCommand 

Dim oracleCommand as ICommand = DatabaseManager.GetDatabase("Oracle").CreateCommand 
2

該模式的主要好處是,它使清潔和可重用的代碼由去耦對象的創建對象使用。

不幸的是,我沒有C#示例,但本週我有一個示例用例是設計一個需要打開套接字的對象。這是在Android上,某些版本不正確地實施SSL/TLS。

要解決這個問題,在Android的這些版本上,我們必須對SSL環境進行大量定製,才能成功與後端交談。使用抽象工廠允許我們編寫套接字客戶端,以便它不需要知道任何有關混亂細節的任何信息 - 它只有一個工廠來獲取套接字。

一個例子:

// this is pretty gross, but what can you do 
public class SocketFactorySupplier implements Supplier<SSLSocketFactory> { 
    @Override public SSLSocketFactory get() { 
    if (androidVersion >= 2.1 && androidVersion <= 2.6) { 
     return createShiftyWorkaround(); 
    } else { 
     return getDefaultSocketFactory(); 
    } 
    } 

    // here are ~500 lines of SSL code 
} 

... 

public class NetworkClient { 
    private final Supplier<SSLSocketFactory> supplier; 
    private Socket socket; 

    public NetworkClient(Supplier<SSLSocketFactory> supplier) { 
    this.supplier = supplier; 
    } 

    public void connect() { 
    socket = supplier.get().createSocket(); 
    socket.connect(); 

    // code that doesn't care about SSL at all and is simpler for it 
    } 
} 

這顯然不是真正的代碼,但它表明了抽象工廠模式的主要優點:

  • NetworkClient代碼清潔,因爲它不關心如何構建套接字
  • 客戶端可以很容易地通過提供模擬套接字進行測試,將其與網絡隔離
  • SSL邏輯可以在需要的插座
  • 等其他類重用
1

工廠的主要優點是,它們允許調用對象保持簡單,並沒有改變,當新的功能添加到應用程序。

假設您有一個簡單的購物車UI,並且您的公司有一個產品。這個非常具體的代碼首先會正常工作。

public class MyShoppingCartUI() 
{ 
    private List<IProduct> _productsInCart = new List<IProduct>(); 

    public void ClickAddProductButton() 
    { 
     IProduct product = new ProductOne(); 
     _productsInCart.Add(product); 
    } 
} 

但是,當您的公司添加新產品時,您將不得不更改您的UI代碼以直接引用新產品。所以,你必須在UI代碼改成這樣:

public class MyShoppingCartUI() 
{ 
    private List<IProduct> _productsInCart = new List<IProduct>(); 

    public void ClickAddProductOneButton() 
    { 
     IProduct product = new ProductOne(); 
     _productsInCart.Add(product); 
    } 

    public void ClickAddProductTwoButton() 
    { 
     IProduct product = new ProductTwo(); 
     _productsInCart.Add(product); 
    } 
} 

正如你所看到的,每次有新產品的時候,你必須添加更多的代碼,你的UI類變得越來越大。爲什麼您的購物車UI取決於哪些產品可用?

這可以通過工廠來解決。這裏是一個分離的代碼示例:

public class ProductFactory() 
{ 
    public IProduct Create(string productName) 
    { 
     if (productName == "Product1") 
      return new ProductOne(); 
     else if (productName == "Product2") 
      return new ProductTwo();    
    } 
} 

public class MyShoppingCartUI() 
{ 
    private ProductFactory _factory = new ProductFactory(); 
    private List<IProduct> _productsInCart = new List<IProduct>(); 

    public void AddItem(string productName) 
    { 
     IProduct product = _factory.Create(productName); 
     _productsInCart.Add(product); 
    } 
} 

這樣一來,無論你有多少個產品添加,你從來沒有改變UI代碼,因爲它並不關心你做什麼樣的產品。您可以將所有控件與產品名稱或ID的列表綁定,並且工廠將爲您提供這些對象。

另一個優點是其餘的應用程序也可以使用Factory來獲取它的Product對象,因此您不必單獨維護創建Product對象的代碼。無論您的應用程序有多大,工廠類中只有1-2行將永遠覆蓋您。

+1

這是'Factory'的一個很好的例子,但是RaulMonteroC特別詢問了「抽象工廠」,這有些不同。 – 2014-10-10 03:34:03

+0

的確如此。但是,考慮到他的要求,似乎他不瞭解的部分就是工廠方面。所以,這就是我的解釋所關注的。如果OP詢問工廠和抽象工廠之間的區別,我會專注於這些細節。 – 2014-10-10 04:44:24

1

抽象工廠模式是一個沒有多大意義的學術範例。我更喜歡根據設計模式而不是它們如何實現,而是想要解決它們的問題。在這種情況下,抽象工廠模式適用於工廠需要根據編譯時不可用的信息進行更改的情況。以不同的方式陳述,當您希望工廠根據只有在運行時纔可用的信息創建不同類型的產品時,它非常有用。功能差異包含在工廠產品本身中,而不是消耗它們的代碼。

我見過的最好的學術範例是在單元測試中。當您測試工廠產品的消費者時,您通常不希望產品的完整重量級功能,而是需要裸機填充程序,以便您可以隔離消費者的邏輯。用工廠模式,這是行不通的。隨着抽象工廠模式,這是微不足道的。您有兩家工廠,一家生產虛擬物品,另一家生產真正的產品,而不是一直使用單一工廠。虛擬工廠在單元測試運行時使用,真正的工廠在正常運行時使用。

另一個很好的例子是跨平臺支持。您可能希望工廠生產不同的產品,具體取決於您是在OSX,Windows,Linux,IOS,Android等平臺上運行。抽象工廠允許您爲每個操作系統實現工廠,並在運行時選擇適當的工廠。

這是非常簡單的用例。額外的抽象層允許您將多種OO設計策略應用到工廠層次結構中,最明顯的是繼承。我發現抽象工廠在與dependency injection結合使用時特別強大,無論您是在受管理的容器(如Unity)內還是手工(這仍然是一個很好的設計實踐)。