Gof定義
定義一種用於創建對象的藉口,讓子類決定實例化哪一個類,Factory Method使得一個類的實例化延遲到子類。
動機
在軟件系統中,經常面臨着“某個對象”的創建工作;由於需求的變化,這個對象經常面臨着劇烈的變化,但是它卻擁有比較穩定的藉口。
假設有這樣的一個場景,有一個汽車類和一個汽車測試框架類,汽車測試框架負責對汽車來進行測試,通常情況下我們會想下面這樣寫。
public class Car { public void Startup() { } public void Run(){ } public void Turn(Direction direction) { } public void Stop() { } }
測試框架類
class CarTestFramework { public void BuildTestContext() { Car car = new Car(); //do something } public void DoTest() { Car car = new Car(); //do something } public void GetTestData() { Car car = new Car(); //do something } }
在上面的測試框架類CarTestFramework中的每個方法都有可能去實例化Car類。上面的代碼中是直接實例化的 Car類。這樣測試框架類和汽車之間有很強的依賴關係。實際中我們的測試框架類不可能只測試一種類型的汽車,所以當被測試的車的類型發生變化時,測試框架類中也要變化,這當然不是我們想要的。現在就把汽車類給抽象起來。
public abstract class AbstractCar { public abstract void Startup(); public abstract void Run(); public abstract void Turn(Direction direction); public abstract void Stop(); }
抽象的汽車類創建了,那麼測試框架類也會發生相應的變化,我們可能會很快地拿AbstractCar特換掉Car,如下:
class CarTestFramework { public void BuildTestContext() { AbstractCar car = new AbstractCar(); //do something } //.... }
很容易可以看出來,上面的代碼其實是錯誤的,抽象類不能實例化。這時可能還會想到另外一種方法用抽象類來實例化子類。
class CarTestFramework { public void BuildTestContext() { AbstractCar car=new Car(); //do something } //...... }
但這樣做還是有問題,實例化時還是用到了具體的類Car,這樣還是對Car產生了依賴。所以就需要有一個工廠類專門來創建對象。下面就創建工廠類CarFactory
public class CarFactory { public AbstractCar CreateCar() { return new Car(); } }
測試框架類的代碼如下:
class CarTestFramework { public void BuildTestContext(CarFactory carFactory) { AbstractCar car1 = carFactory.CreateCar(); AbstractCar car2 = carFactory.CreateCar(); AbstractCar car3 = carFactory.CreateCar(); //...不管需要幾個用工廠類的方法創建就可以了 } //........ }
在客戶程序中像下面這樣調用
public class App { public void Main() { CarTestFramework carTestFramework = new CarTestFramework(); carTestFramework.BuildTestContext(new CarFactory()); //.... } }
上面的代碼可以看出傳入到測試框架類中方法的參數是一個工廠類的對象,而在工廠類的方法CreateCar中是直接返回的Car類,這樣耦合的關係又移到了工廠類的CreateCar中,產生了強依賴。假設現在有個HongqiCar<需要被測試,就需要更改CreateCar方法,如下:
public class HongqiCar:AbstractCar { public override void Startup() { } public override void Run() { } public override void Turn(Direction direction) { } public override void Stop() { } }
更改後的工廠類
public class CarFactory { public AbstractCar CreateCar() { return new HongqiCar(); } }
這樣的設計顯然也是不好的。既然強依賴發生在工廠類中,就可以將工廠類也抽象起來
public abstract class AbstractCarFactory { public abstract AbstractCar CreateCar(); }
上面說到有HongqiCar需要被測試,就創建一個生成HongqiCar的工廠類,這個類繼承抽象工廠類。
public class HongqiCarFactory : AbstractCarFactory { public override AbstractCar CreateCar() { return new HongqiCar(); } }
現在客戶程序就可以改成這樣
public class App { public void Main() { CarTestFramework carTestFramework = new CarTestFramework(); carTestFramework.BuildTestContext(new HongqiCarFactory()); //.... } }
這樣如果又有新的需求,比如要添加AutiCar進行測試,只需要做下面幾步
1 添加AutiCar類繼承AbstractCar類
public class AudiCar : AbstractCar { public override void Startup() { } public override void Run() { } public override void Turn(Direction direction) { } public override void Stop() { } }
2 添加AudiCar的工廠類繼承AbstractCarFactory類
public class AudiCarFactory : AbstractCarFactory { public override AbstractCar CreateCar() { return new AudiCar(); } }
3 客戶程序稍作改動即可
public class App { public void Main() { CarTestFramework carTestFramework = new CarTestFramework(); carTestFramework.BuildTestContext(new HongqiCarFactory()); //添加一行代碼即可 carTestFramework.BuildTestContext(new AudiCarFactory()); //.... } }
完整的代碼
AbstractCar.cs
/// <summary> /// 抽象汽車類 /// </summary> public abstract class AbstractCar { public abstract void Startup(); public abstract void Run(); public abstract void Turn(Direction direction); public abstract void Stop(); }
AbstractCarFactory.cs
/// <summary> /// 抽象工廠類 /// </summary> public abstract class AbstractCarFactory { public abstract AbstractCar CreateCar(); }
CarTestFramework.cs
/// <summary> /// 測試框架類 /// </summary> public class CarTestFramework { public void BuildTestContext(AbstractCarFactory abstractCarFactory) { AbstractCar car = abstractCarFactory.CreateCar(); } public void DoTest(AbstractCarFactory abstractCarFactory) { //do something } public void GetTestData(AbstractCarFactory abstractCarFactory) { //do something } }
App.cs
/// <summary> /// 客戶類 /// </summary> public class App { public void Main() { CarTestFramework carTestFramework = new CarTestFramework(); carTestFramework.BuildTestContext(new HongqiCarFactory()); //.... } }
上面大的就是基本的代碼,如果需要什麼類型的汽車被測試,只需要添加兩個具體類去繼承抽象的汽車類和抽象工廠類,然後在客戶程序稍作修改既可,並且客戶程序也可以通過反射加配置文件的方式而不需要做任何修改。這樣就很好滿足了OCP原則,需求變動只要擴展新的類就可以。
Factory Method設計模式的幾個要點
Factory Method模式主要用於隔離類對象的使用者和具體類型之間的耦合關係。面對一個經常變化的具體類型,緊耦合關係會導致軟件的脆弱。
Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好解決了這種緊耦合關係。
Factory Method模式解決“單個對象”的變化,Abstract Factory模式解決了“系列對象”的需求變化,Builder模式解決了“對象部分”的需求變化。