Factory 方法(Factory Method)模式
Factory 方法(FactoryMethod)模式是類的建立模式,其用意是定義一個建立產品對象的工廠介面,將實際建立工作延遲到子類中。
Factory 方法模式是簡單原廠模式的進一步抽象和推廣。由於使用了多態性,Factory 方法模式保持了簡單原廠模式的優點,而且克服了它的缺點。
在Factory 方法模式中,核心的工廠類不再負責所有產品的建立,而是將具體建立工作交給子類去做。這個核心類僅僅負責給出具體工廠必須實現的介面,而不接觸哪一個產品類被執行個體化這種細節。這使得Factory 方法模式可以允許系統在不修改工廠角色的情況下引進新產品。
在Factory Method模式中,工廠類與產品類往往具有平行的等級結構,它們之間一一對應。
二、 Factory Method模式角色與結構:
抽象工廠(Creator)角色:是Factory 方法模式的核心,與應用程式無關。任何在模式中建立的對象的工廠類必須實現這個介面。
具體工廠(Concrete Creator)角色:這是實現抽象工廠介面的具體工廠類,包含與應用程式密切相關的邏輯,並且受到應用程式調用以建立產品對象。在中有兩個這樣的角色:BulbCreator與TubeCreator。
抽象產品(Product)角色:Factory 方法模式所建立的對象的超類型,也就是產品對象的共同父類或共同擁有的介面。在中,這個角色是Light。
具體產品(Concrete Product)角色:這個角色實現了抽象產品角色所定義的介面。某具體產品有專門的具體工廠建立,它們之間往往一一對應。
三、 程式舉例:
using System;public abstract class Light{ public abstract void TurnOn(); public abstract void TurnOff();}public class BulbLight : Light{ public override void TurnOn() { Console.WriteLine("Bulb Light is Turned on"); } public override void TurnOff() { Console.WriteLine("Bulb Light is Turned off"); }}public class TubeLight : Light{ public override void TurnOn() { Console.WriteLine("Tube Light is Turned on"); } public override void TurnOff() { Console.WriteLine("Tube Light is Turned off"); }}public abstract class Creator{ public abstract Light factory();}public class BulbCreator : Creator{ public override Light factory() { return new BulbLight(); }}public class TubeCreator : Creator{ public override Light factory() { return new TubeLight(); }}public class Client{ public static void Main() { Creator c1 = new BulbCreator(); Creator c2 = new TubeCreator(); Light l1 = c1.factory(); Light l2 = c2.factory(); l1.TurnOn(); l1.TurnOff(); Console.WriteLine("-----------------"); l2.TurnOn(); l2.TurnOff(); }}
Factory 方法的活動順序圖表
活動過程包括:
用戶端建立BulbCreator對象,用戶端持有此對象的類型是Creator,而實際類型是BulbCreator。然後用戶端調用BulbCreator的factory方法,之後BulbCreator調用BulbLight的建構函式創造出產品BulbLight對象。
四、 Factory 方法模式與簡單原廠模式
Factory 方法模式與簡單原廠模式再結構上的不同不是很明顯。Factory 方法類的核心是一個抽象工廠類,而簡單原廠模式把核心放在一個具體類上。
Factory 方法模式之所以有一個別名叫多態性原廠模式是因為具體工廠類都有共同的介面,或者有共同的抽象父類。
當系統擴充需要添加新的產品對象時,僅僅需要添加一個具體對象以及一個具體工廠對象,原有工廠對象不需要進行任何修改,也不需要修改用戶端,很好的符合了"開放-封閉"原則。而簡單原廠模式在添加新產品對象後不得不修改Factory 方法,擴充性不好。
Factory 方法模式退化後可以演變成簡單原廠模式。
五、 Factory Method模式演化
使用介面或抽象類別
抽象工廠角色和抽象場頻角色都可以選擇由介面或抽象類別實現。
使用多個Factory 方法
抽象工廠角色可以規定出多於一個的Factory 方法,從而使具體工廠角色實現這些不同的Factory 方法,這些方法可以提供不同的商業邏輯,以滿足提供不同的產品對象的任務。
產品的迴圈使用
Factory 方法總是調用產品類的建構函式以建立一個新的產品執行個體,然後將這個執行個體提供給用戶端。而在實際情形中,Factory 方法所做的事情可以相當複雜。
一個常見的複雜邏輯就是迴圈使用產品對象。工廠對象將已經建立過的產品登記到一個聚集中,然後根據客戶所請求的產品狀態,向聚集查詢。如果有滿足要求的產品對象,就直接將產品返回用戶端;如果聚集中沒有這樣的產品對象,那麼就建立一個新的滿足要求的產品對象,然後將這個對象登記到聚集中,再返還給用戶端。"享元模式(Flyweight Pattern)"就是這樣一個模式。
多態性的喪失和模式的退化
一個Factory 方法模式的實現依賴於工廠角色和產品角色的多態性。在有些情況下,這個模式可以出現退化。
Factory 方法返回的類型應當是抽象類別型,而不是具體類型。調用Factory 方法的用戶端應當依賴抽象產品編程,而不是具體產品。如果工廠僅僅返回一個具體產品對象,便違背了Factory 方法的用意,發生退化,這時就不再是原廠模式了。
工廠的等級結構:工廠對象應當有一個抽象的超類型。如果等級結構中只有一個具體工廠類的話,抽象工廠就可以省略,發生了退化。
六、 Factory Method模式與其它模式的關係
與Factory 方法模式有關的模式還包括:
模板方法模式、MVC模式、享元模式、備忘錄模式
七、 另外一個例子
// Factory Method pattern -- Real World example using System;using System.Collections;// "Product"abstract class Page{}// "ConcreteProduct"class SkillsPage : Page{}// "ConcreteProduct"class EducationPage : Page{}// "ConcreteProduct"class ExperiencePage : Page{}// "ConcreteProduct"class IntroductionPage : Page{}// "ConcreteProduct"class ResultsPage : Page{}// "ConcreteProduct"class ConclusionPage : Page{}// "ConcreteProduct"class SummaryPage : Page{}// "ConcreteProduct"class BibliographyPage : Page{}// "Creator"abstract class Document{ // Fields protected ArrayList pages = new ArrayList(); // Constructor public Document() { this.CreatePages(); } // Properties public ArrayList Pages { get { return pages; } } // Factory Method abstract public void CreatePages();}// "ConcreteCreator"class Resume : Document{ // Factory Method implementation override public void CreatePages() { pages.Add(new SkillsPage()); pages.Add(new EducationPage()); pages.Add(new ExperiencePage()); }}// "ConcreteCreator"class Report : Document{ // Factory Method implementation override public void CreatePages() { pages.Add(new IntroductionPage()); pages.Add(new ResultsPage()); pages.Add(new ConclusionPage()); pages.Add(new SummaryPage()); pages.Add(new BibliographyPage()); }}/**//// <summary>/// FactoryMethodApp test/// </summary>class FactoryMethodApp{ public static void Main(string[] args) { Document[] docs = new Document[2]; // Note: constructors call Factory Method docs[0] = new Resume(); docs[1] = new Report(); // Display document pages foreach (Document document in docs) { Console.WriteLine(" " + document + " ------- "); foreach (Page page in document.Pages) Console.WriteLine(" " + page); } }}