在五大設計原則的基礎上經過GOF(四人組)的總結,得出了23種經典設計模式,其中分為三大類:創建型(5種)、結構型(7種)、行為型(11種)。今天對創建型中的工廠方法(FactoryMethod)模式的思想進行了一下復習和實踐,在此也做一下記錄。同樣,理解或實踐不到位的地方,希望走過路過的看官指正一下!
同樣先來看看工廠方法(FactoryMethod)模式的定義:
Define an interface for creating an object, but let subclasses decide which class to instantiate.
意思就是說:定義一個用於創建對象的接口,但讓子類決定要實例化哪個類。
也就是工廠方法(FactoryMethod)模式允許將產品類的實例化推遲到具體的創建者子類,由創建者子類決定實例化哪一個產品類。我們同樣以汽車的生產作為講解該模式的例子,因為汽車生產從宏觀上來說也是特別符合這個模式的。我要一輛汽車,什么品牌的我沒有要求,符合是汽車這個標准就行了。那么世面上不同的汽車生產商就會用自己的生產流程生產出符合汽車這個標准的不同品牌汽車。同樣,我們也擼起袖子碼一碼!因為該模式沒有強調待生產的產品類是復雜的,同樣也為了減少大家的代碼閱讀,這次我們把汽車相關類定義的更簡單一點!
由於是讓子類決定要實例化哪個產品類,那么高層的類或客戶代碼肯定是不需要知道有哪些具體產品類的,也就是得為眾多具體的產品類制定一個產品標准(可以是接口,也可以是抽象類),這里我們定義一個汽車接口——ICar,它只有一個啟動接口方法:
/// <summary> /// 汽車接口 /// </summary> public interface ICar { /// <summary> /// 啟動汽車 /// </summary> /// <returns>是否啟動成功</returns> bool Start(); }
有了汽車產品標准(ICar),我們在此基礎上定義兩個實現了該標准的具有汽車產品類——奔馳(Benz)和寶馬(BWM),它們對汽車產品標准中的啟動標准做了不一樣的實現(機械鑰匙啟動和按鍵啟動):
/// <summary> /// 奔馳汽車 /// </summary> [Serializable] public class BenzCar : ICar { private readonly string name = "Benz"; private readonly string model; /// <summary> /// 車名 /// </summary> public string Name { get { return name; } } /// <summary> /// 型號 /// </summary> public string Model { get { return model; } } /// <summary> /// 初始化奔馳汽車 /// </summary> public BenzCar() { } /// <summary> /// 初始化奔馳汽車(帶簡單參數) /// </summary> /// <param name="model"></param> public BenzCar(string model) { this.model = model; } /// <summary> /// 啟動奔馳汽車 /// </summary> /// <returns>是否啟動成功</returns> public bool Start() { Console.WriteLine("【{0} {1}】通過機械鑰匙啟動!", Name, Model); return true; } } /// <summary> /// 寶馬汽車 /// </summary> [Serializable] public class BwmCar : ICar { private readonly string name = "BWM"; private readonly string model; /// <summary> /// 車名 /// </summary> public string Name { get { return name; } } /// <summary> /// 型號 /// </summary> public string Model { get { return model; } } /// <summary> /// 初始化寶馬汽車 /// </summary> public BwmCar() { } /// <summary> /// 初始化寶馬汽車(帶簡單參數) /// </summary> /// <param name="model"></param> public BwmCar(string model) { this.model = model; } /// <summary> /// 啟動寶馬汽車 /// </summary> /// <returns>是否啟動成功</returns> public bool Start() { Console.WriteLine("【{0} {1}】通過按鍵啟動!", Name, Model); return true; } }
有了汽車產品標准,也有了兩個具體的汽車產品類型,生產商還沒有,生產標准也還沒有!回顧下工廠方法模式的定義:定義一個用於創建對象的接口,但讓子類決定要實例化哪個類!這里的“用於創建對象的接口”其實就是生產標准,而“子類”就是實現了生產標准的具體生產商。在我們的例子中就是汽車生產者接口(標准)和實現了該標准的具體汽車生產商:
/// <summary> /// 汽車生產接口(標准、規范) /// </summary> public interface ICarCreator { /// <summary> /// 工廠級別的汽車生產接口方法(工廠方法模式的精髓所在!) /// </summary> /// <returns>生產好的汽車</returns> ICar ProduceCar(); } /// <summary> /// 奔馳生產者 /// </summary> public class BenzCreator : ICarCreator { /// <summary> /// 奔馳生產方法(對生產過程只做了簡單模擬) /// </summary> /// <returns>奔馳汽車</returns> public ICar ProduceCar() { ICar car = new BenzCar("梅賽德斯-AMG GLC 43 4MATIC"); Console.WriteLine("【Benz 梅賽德斯-AMG GLC 43 4MATIC】生產開始:\r1、生產發動機;\r2、生產底盤;\r3、生產車身;\r4、生產變速箱;\r5、生產輪胎;\r6、組裝;"); Console.WriteLine("【Benz 梅賽德斯-AMG GLC 43 4MATIC】生產完成!"); return car; } } /// <summary> /// 寶馬生產者 /// </summary> public class BwmCreator : ICarCreator { /// <summary> /// 寶馬生產方法(對生產過程只做了簡單模似) /// </summary> /// <returns>寶馬汽車</returns> public ICar ProduceCar() { ICar car = new BwmCar("X7"); Console.WriteLine("【BWM X7】生產開始:\r1、生產底盤;\r2、生產發動機;\r3、生產變速箱;\r4、生產車身;\r5、生產輪胎;\r6、組裝;"); Console.WriteLine("【BWM X7】生產完成!"); return car; } }
我們可以看到,對於汽車生產標准,實現了該標准的不同汽車生產者可以自行決定生產什么類型的汽車產品以及如何生產——讓子類決定要實例化哪個類!
到目前為止,工廠方法(FactoryMethod)模式中各個角色都已經出現:用於創建對象的接口——汽車生產者接口(ICarCreator);子類(即具體的創建者)——奔馳生產者(BenzCreator)和寶馬生產者(BwmCreator);要實例化的類(產品對象)——遵循統一產品標准(ICar)的奔馳汽車(BenzCar)和寶馬汽車(BwmCar)。
接下來我們來測試一下這樣的對象創建構造符不符合工廠方法(FactoryMethod)模式的定義:定義一個用於創建對象的接口,但讓子類決定要實例化哪個類。
[TestClass] public class FactoryMethodTest { [TestMethod] public void TestFactoryMethod() { ICarCreator carCreator = new BenzCreator();//先將汽車創建者指向奔馳(Benz)汽車的創建者 ICar car = carCreator.ProduceCar();//由奔馳(Benz)汽車創建者決定生產什么車(實例化哪個類) Assert.IsTrue(car.Start()); Console.Write("\r\r"); carCreator = new BwmCreator();//再將汽車創建者指向寶馬(BWM)汽車的創建者 car = carCreator.ProduceCar();//再由寶馬(BWM)汽車創建者決定生產什么車(實例化哪個類) Assert.IsTrue(car.Start()); } }
可以看到客戶代碼已經可以讓子類決定要實例化哪個類。我們最后來看看測試輸出結果:

最后,再次注意一下工廠方法(FactoryMethod)模式和構建者(Builder)模式的區別:
- 對要生產的產品制定了產品標准(IProduct),構建者模式中這個並不是必要的,當然也可以制定;
- 產品創建者標准(ICreator)只提供一個粗粒度的標准產品生產接口(FactoryMethod);
- 沒有了額外的生產指導者,產品的具體創建流程和細節由具體的產品創建者(ConcreteCreator)自己決定;
工廠方法(FactoryMethod)模式的使用場景在於模式定義的后半句:讓子類決定要實例化哪個類(子類是指創建者子類)。也就是當你在開發過程中,對於創建對象有這樣的需求時可以考慮一下工廠方法模式!而構建者模式主要使用場景在於用相同的構建過程構建復雜對象的不同表示。
