[Design Pattern] 簡單工廠模式 (Simple Factory Pattern) 不怕飲料有幾種

介紹設計模式,以飲料店的情境說明如何使用簡單工廠模式 (Simple Factory Pattern) 封裝降低類別耦合性。

前言


  繼裝飾者模式後接下來講工廠模式,首先先來談簡單工廠模式,工廠模式算一個很常使用到的設計模式,而簡單工廠模式算是最基本的工廠模式,在 Head First Design Patterns 中也提到更多的情況下可以當作是一種編成習慣,接下來讓我用飲料店的情境來加以說明。

 

實作簡單工廠模式


  假設我是一間只賣綠茶的飲料店,客人買了一杯綠茶時我們會這樣做,如下


public GreenTea TeaOrders()
{
    GreenTea greenTea = new GreenTea();
    greenTea.AddMaterial(); // 加料
    greenTea.Brew(); // 沖泡
    greenTea.PouredCup(); // 裝杯
    return greenTea;
}

 

  但是,如果只賣綠茶已經不能應付客人想多選擇的需求,我們就必須增加更多個飲料品項,現在我們增加了紅茶供客人選擇,如下


// 綠茶
public GreenTea GreenTeaOrders()
{
    GreenTea greenTea = new GreenTea();
    greenTea.AddMaterial(); // 加料
    greenTea.Brew(); // 沖泡
    greenTea.PouredCup(); // 裝杯
    return greenTea;
}

// 紅茶
public BlackTea BlackTeaOrders()
{
    BlackTea blackTea = new BlackTea();
    blackTea.AddMaterial(); // 加料
    blackTea.Brew(); // 沖泡
    blackTea.PouredCup(); // 裝杯
    return blackTea;
}

 

  這樣客人就多了紅茶可以選擇,可是這裡我突然想到依照前幾篇的設計模式經驗,當飲料品項繼續增加的時候,我的 BeverageStores 類別就必須加入對應的飲品方法,但是這些飲品方法內做的事情其實是一樣的,我們可以發現 AddMaterial() 、Brew() 、 PouredCup() 三個方法都是固定必須處理的事情,這時候我們就可以透過使用介面將這些動作進行封裝,所以我們將這三個方法加入介面中,如下


public interface IBeverageProvide
{
    void AddMaterial();
    void Brew();
    void PouredCup();
}

 

  接著必須將飲料類別實作介面方法後再調整產生飲料的方法,如下


public class GreenTea : IBeverageProvide
{
}

public class BlackTea : IBeverageProvide
{
}

public IBeverageProvide BeverageOrders(string pBeverageType)
{
    IBeverageProvide beverage;
    if (pBeverageType == "GreenTea")
        beverage = new GreenTea();
    if (pBeverageType == "BlackTea")
        beverage = new BlackTea();
    else
        return null;

    beverage.AddMaterial(); // 加料
    beverage.Brew(); // 沖泡
    beverage.PouredCup(); // 裝杯

    return beverage;
}

 

  現在透過回傳 IBeverageProvide 介面的方式,我可以不用再增加多餘的飲品方法了,但這時又產生了新的問題,我想到在 OO 的守則下因遵循「類別應該開放便於擴充、應該關閉禁止修改」,但由於我們是使用 IF ELSE 與 new 關鍵字來實體化對象類別,所以當增加新飲品時修改應是不可避免的,但是我們已經知道了哪些地方是必須要修改的,所以我應該將這些地方提出來進行封裝,以減少 BeverageStores 類別對於飲料類別的相依性。

 

  我再次調整 BeverageOrders 類別,將產生實體化的部分提取出到一個新的 SimpleBeverageFactory 類別中,如下


public class SimpleBeverageFactory
{
    public IBeverageProvide CreateBeverage(string pBeverageType)
    {
        IBeverageProvide beverage;
        if (pBeverageType == "GreenTea")
            return beverage = new GreenTea();
        if (pBeverageType == "BlackTea")
            return beverage = new BlackTea();
        else
            return null;
    }
}

 

  由以上程式碼可以看到 SimpleBeverageFactory 類別將產生飲品實體的邏輯置入 CreateBeverage 方法中,透過將產生實體的邏輯區段提出後放置到另一個類別中,我們就可以稱此類別為一個工廠類別,由這個類別的 CreateBeverage 方法來決定要產生哪一個飲品類別,日後如要增加新飲品類別就由此處修改即可。

 

  接著我們需要調整原本的 BeverageStores 類別,讓此類別能夠透過傳入對應的工廠類別建立飲品的實體,如下


public class BeverageStores
{
    private SimpleBeverageFactory  _factory;

    public BeverageStores(SimpleBeverageFactory pFactory)
    {
        _factory = pFactory;
    }

    public IBeverageProvide BeverageOrders(string pBeverageType)
    {
        IBeverageProvide beverage;
        beverage = _factory.CreateBeverage(pBeverageType);

        beverage.AddMaterial(); // 加料
        beverage.Brew(); // 沖泡
        beverage.PouredCup(); // 裝杯

        return beverage;
    }
}

 

  而套用工廠模式前的類別圖如下,BeverageStores 類別直接關聯了 GreenTea 類別與 BlackTea 類別

 

  而透過介面與簡單工廠模式封裝後如下,BeverageStores 類別切開了與 GreenTea 類別及 BlackTea 類別的關聯性,讓 BeverageStores 類別改關聯於 SimpleBeverageFactory 工廠類別,SimpleBeverageFactory 工廠類別則透過 IBeverageProvide 飲品介面切開與飲品類別的關聯,降低了各類別的耦合性。

 

 

  最後讓我們來製作飲料給客人吧 :P


static void Main(string[] args)
{
    BeverageStores store = new BeverageStores(new SimpleBeverageFactory());
    Console.WriteLine("A 客人點了綠茶");
    store.BeverageOrders("GreenTea");
    Console.WriteLine("B 客人點了紅茶");
    store.BeverageOrders("BlackTea");
    Console.ReadLine();
}

 

範例程式碼 2016/09/24 補檔


https://drive.google.com/open?id=0B40daTESrAXwRTk4S09KNVUycDg

 

參考資料


Head First Design Patterns 深入淺出設計模式




以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)