介紹設計模式,以飲料店的情境說明如何使用簡單工廠模式 (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 深入淺出設計模式
以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)