產生"一系列"性質相關或相依的物件
官方說明
提供一個介面,建立相關或相依物件之家族,而不需要明確指定具體類別。
主要療效
透過ConcreteFactory1來產生粉紅系列的商品: ProductA1, ProductB1
透過ConcreteFactory2來產生彩橘系列的商品: ProductA2, ProductB2
適用情境
1. 系統中有多組"相同性質"之物件集合
2. 系統只會使用其中一組"相同性質"之物件集合
3. 集合內之物件間是相關或相依的關係
4. 集合內之物件必定是在一起使用
PS. 如果沒有多組"相同性質"之物件集合需求時,使用靜態工廠即可
關鍵心法
1. 建立產品抽象(abstract/interface)類別
2. 建立各產品之不同系列類別 (繼承產品抽象類別)
3. 建立抽象工廠 (定義一整組生產商品物件的方法)
4. 建立各系列專屬的工廠 (繼承抽象工廠且實作所有方法[產生此系列產品物件實體])
應用聯想
想像遊戲軟體開發需求,初始畫面可依遊戲玩家國籍來顯示遊戲場景,場景需呈現出具該國風格之街道景色。分析一下,各國的街景似乎大同小異,不外乎市街道、招牌、房子等物件存在,且各場景中物件都具有相關性(日式街道+日式招牌+日式屋型),該些特性似乎都滿符合此模式之適用情境,因此我們將實際演練如何透過抽象工廠模式的協助,產出這一系列相關性物件。
首先來檢查是否合乎適用情境:
1. 系統中有多組"相同性質"之物件集合
台灣場景 or 日本場景
2. 系統只會使用其中一組"相同性質"之物件集合
只會使用一種場景顯示
3. 集合內之物件間是相關或相依的關係
日本街景 只與 日本招牌 互相依賴作用
4. 集合內之物件必定是在一起使用
不會一個使用日本街景 另一個使用台灣招牌吧!!
搭配關鍵心法來實現遊戲場景的抽象工廠:
1. 建立產品抽象(abstract/interface)類別
筆者希望遊戲場景中有街道並且佈滿著招牌,所以先定義出這兩者之介面類別
其中需要實作 GetDescription() 方法來簡單描述實際呈現出的樣貌,如下:
public interface ISignboardScene
{
// 取得招牌描述字串
string GetSignboardDescription();
}
// 街道介面
public interface IStreetScene
{
// 取得街道描述字串
string GetStreetDescription();
}
2. 建立各產品之不同系列類別 (繼承產品抽象類別)
招牌: 日本招牌、台灣招牌...
class JapanSignboardScene : ISignboardScene
{
public string GetSignboardDescription()
{
return "日文招牌";
}
}
class TaiwanSignboardScene : ISignboardScene
{
public string GetSignboardDescription()
{
return "中文招牌";
}
}
街道: 日本街道、台灣街道 ...
class JapanStreetScene : IStreetScene
{
public string GetStreetDescription()
{
return "很多日本人走在路上";
}
}
class TaiwanStreetScene : IStreetScene
{
public string GetStreetDescription()
{
return "很多台灣人走在路上";
}
}
3. 建立抽象工廠 (定義一整組生產商品物件的方法)
筆者希望一個場景中至少需要街道與招牌來作呈現
所以繼承場景抽象工廠之子類別皆須實作CreateSignboardScene()與CreateStreetScene()方法
public abstract class AbstractSceneFactory
{
// 產生招牌
public abstract ISignboardScene CreateSignboardScene();
// 產生街道
public abstract IStreetScene CreateStreetScene();
}
4. 建立各系列專屬的工廠 (繼承抽象工廠且實作所有方法[產生此系列產品物件實體])
日本場景工廠: 需產生 日本街道 + 日本招牌
public class JapanSceneFactory : AbstractSceneFactory
{
public override ISignboardScene CreateSignboardScene()
{
// 產生日本招牌
return new JapanSignboardScene();
}
public override IStreetScene CreateStreetScene()
{
// 產生日本街道
return new JapanStreetScene();
}
}
台灣場景工廠: 需產生 台灣街道 + 台灣招牌
public class TaiwanSceneFactory : AbstractSceneFactory
{
public override ISignboardScene CreateSignboardScene()
{
// 產生台灣招牌
return new TaiwanSignboardScene();
}
public override IStreetScene CreateStreetScene()
{
// 產生台灣街道
return new TaiwanStreetScene();
}
}
使用場景抽象工廠來產生不同系列之場景:
首先須建立場景工廠實體(此例為日本場景工廠),透過注入的方式讓ShowDescriptScene()方法中取得工廠實體,並且透過工廠產生一系列場景物件(日式招牌、日式街道);若需要呈現台灣場景時,僅需修改注入台灣廠場景工廠實體即可。
class Program
{
static void Main(string[] args)
{
// 建立工廠 (日本場景工廠)
var factory = new JapanSceneFactory();
// 透過注入(DI)之工廠產生遊戲場景
ShowSceneDescription(factory);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
// 顯示場景描述 (DI: 方法注入)
public static void ShowSceneDescription(AbstractSceneFactory factory)
{
// 使用抽象工廠 產出的同系列產品
ISignboardScene signboardtScene = factory.CreateSignboardScene();
IStreetScene streetScene = factory.CreateStreetScene();
// 輸出 街景介紹 結果
string sceneDescription = string.Empty;
sceneDescription += string.Format("招牌: {0} \n", signboardtScene.GetSignboardDescription());
sceneDescription += string.Format("街道: {0} \n", streetScene.GetStreetDescription());
Console.WriteLine("街景介紹");
Console.WriteLine(sceneDescription);
// 整個程式只與介面相依(AbstractSceneFactory, ISignboardScene, IStreetScene)
// 與OOP中針對介面寫程式的概念相符
}
}
後記:
我們可以發現整個程式只與介面相依(AbstractSceneFactory, ISignboardScene, IStreetScene),與OOP中針對介面寫程式的概念相符;另外亦符合OOP中開放封閉原則,成功將ShowSceneDescription透過注入方式進行封閉,將來若須建立美國場景時,只需實作美國招牌(ISignboardScene)與街道(IStreetScene),並實作出美國場景工廠(AbstractSceneFactory)來進行注入,即可輕易地於遊戲中顯示美式風格場景。
參考資料
http://www.dotblogs.com.tw/mumbo/archive/2011/06/29/30406.aspx
http://www.cnblogs.com/Terrylee/archive/2005/12/13/295965.html
希望此篇文章可以幫助到需要的人
若內容有誤或有其他建議請不吝留言給筆者喔 !