本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7567880.html,記錄一下學習過程以備後續查用。 一、引言 接上一篇C#設計模式學習筆記:簡單工廠模式(工廠方法模式前奏篇),通過簡單工廠模式的瞭解,它的缺點就是隨著需求的變化我們要不停地修改工廠里 面的 ...
本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7567880.html,記錄一下學習過程以備後續查用。
一、引言
接上一篇C#設計模式學習筆記:簡單工廠模式(工廠方法模式前奏篇),通過簡單工廠模式的瞭解,它的缺點就是隨著需求的變化我們要不停地修改工廠里
面的方法的代碼,需求變化越多,裡面的if--else也越多,這樣就會造成簡單工廠的實現邏輯過於複雜。
依設計原則里的開閉原則--對增加代碼開放,對修改代碼關閉,我們不能總是這樣修改簡單工廠裡面的方法。
下麵看看工廠方法模式是如何解決該問題的?
二、工廠方法模式介紹
工廠方法模式:英文名稱--Factory Method Pattern;分類--創建型。
2.1、動機(Motivate)
在軟體系統的構建過程中,經常面臨著“某個對象”的創建工作:由於需求的變化,這個對象(的具體實現)經常面臨著劇烈的變化,但是它卻擁有比
較穩定的介面。
如何應對這種變化?如何提供一種“封裝機制”來隔離出“這個易變對象”的變化,從而保持系統中“其他依賴對象的對象”不隨著需求改變而改變?
2.2、意圖(Intent)
定義一個用於創建對象的介面,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲到子類。--《設計模式》GoF
2.3、結構圖(Structure)
2.4、模式的組成
從上圖可以看出,在工廠方法模式的結構圖有以下角色:
1)抽象工廠角色(Creator):充當抽象工廠角色,定義工廠類所具有的基本的操作,任何具體工廠都必須繼承該抽象類。
2)具體工廠角色(ConcreteCreator):充當具體工廠角色,該類必須繼承抽象工廠角色,實現抽象工廠定義的方法,用來創建具體產品。
3)抽象產品角色(Product):充當抽象產品角色,定義了產品類型所有具有的基本操作,具體產品必須繼承該抽象類。
4)具體產品角色(ConcreteProduct):充當具體產品角色,實現抽象產品類對定義的抽象方法,由具體工廠類創建,它們之間有一一對應的關係。
2.5、工廠方法模式代碼實現
面向對象設計三大原則:
1)哪裡有變化就封裝哪裡。
2)面向抽象編程,細節和高層實現都要依賴抽象。
3)多組合,少繼承。
這三大原則是最根本的原則,學習設計模式必須以這三個原則為基點,否則都是枉然。根據這三大原則又衍生出來6個具體的原則,分別是單一職責
原則、開閉原則、里氏替換原則、依賴倒置原則、介面隔離原則、迪米特法則。
既然工廠類有變化,我們就封裝它,面向抽象編程。我們先抽象出一個工廠基類,然後每個需求都實現一個具體的工廠類,這樣我們就符合了開閉原
則,讓一個工廠生產一款產品,並一一對應。具體產品的創建推遲到子類中,此時工廠類(基類)不再負責所有產品的創建,而只是給出具體工廠必須
實現的介面,這樣工廠方法模式就可以允許系統不修改工廠類邏輯的情況下來添加新產品,也就剋服了簡單工廠模式中缺點。
下麵是工廠方法模式的實現代碼:
class Program { /// <summary> /// 汽車抽象類 /// </summary> public abstract class Car { //開始行駛 public abstract void Go(); } /// <summary> /// 紅旗汽車 /// </summary> public class HongQiCar : Car { public override void Go() { Console.WriteLine("紅旗汽車生產中。"); } } /// <summary> /// 奧迪汽車 /// </summary> public class AoDiCar : Car { public override void Go() { Console.WriteLine("奧迪汽車生產中。"); } } /// <summary> /// 抽象工廠類 /// </summary> public abstract class Factory { //工廠方法 public abstract Car CreateCar(); } /// <summary> /// 紅旗汽車工廠類 /// </summary> public class HongQiCarFactory : Factory { /// <summary> /// 負責生產紅旗汽車 /// </summary> /// <returns></returns> public override Car CreateCar() { return new HongQiCar(); } } /// <summary> /// 奧迪汽車工廠類 /// </summary> public class AoDiCarFactory : Factory { /// <summary> /// 負責創建奧迪汽車 /// </summary> /// <returns></returns> public override Car CreateCar() { return new AoDiCar(); } } static void Main(string[] args) { #region 工廠方法模式 //初始化創建汽車的兩個工廠 Factory hongQiCarFactory = new HongQiCarFactory(); Factory aoDiCarFactory = new AoDiCarFactory(); //生產一輛紅旗汽車 Car hongQi = hongQiCarFactory.CreateCar(); hongQi.Go(); //生產一輛奧迪汽車 Car aoDi = aoDiCarFactory.CreateCar(); aoDi.Go(); Console.Read(); #endregion } }View Code
運行結果如下:
使用工廠方法實現的系統,如果系統需要添加新產品時,我們可以利用多態性來完成系統的擴展,對於抽象工廠類和具體工廠中的代碼都不需要做任
何改動。假如我們想生產賓士車,我們只需從Car抽象類下繼承一個BenChiCar類、在Factory抽象類下繼承一個“賓士”的工廠類BenChiCarFactory就可
以實現了:
class Program { /// <summary> /// 汽車抽象類 /// </summary> public abstract class Car { //開始行駛 public abstract void Go(); } /// <summary> /// 紅旗汽車 /// </summary> public class HongQiCar : Car { public override void Go() { Console.WriteLine("紅旗汽車生產中。"); } } /// <summary> /// 奧迪汽車 /// </summary> public class AoDiCar : Car { public override void Go() { Console.WriteLine("奧迪汽車生產中。"); } } /// <summary> /// 賓士汽車 /// </summary> public class BenChiCar : Car { public override void Go() { Console.WriteLine("賓士汽車生產中。"); } } /// <summary> /// 抽象工廠類 /// </summary> public abstract class Factory { //工廠方法 public abstract Car CreateCar(); } /// <summary> /// 紅旗汽車工廠類 /// </summary> public class HongQiCarFactory : Factory { /// <summary> /// 負責生產紅旗汽車 /// </summary> /// <returns></returns> public override Car CreateCar() { return new HongQiCar(); } } /// <summary> /// 奧迪汽車工廠類 /// </summary> public class AoDiCarFactory : Factory { /// <summary> /// 負責創建奧迪汽車 /// </summary> /// <returns></returns> public override Car CreateCar() { return new AoDiCar(); } } /// <summary> /// 賓士汽車工廠類 /// </summary> public class BenChiCarFactory : Factory { /// <summary> /// 負責生產賓士汽車 /// </summary> /// <returns></returns> public override Car CreateCar() { return new BenChiCar(); } } static void Main(string[] args) { #region 工廠方法模式 //初始化創建汽車的兩個工廠 Factory hongQiCarFactory = new HongQiCarFactory(); Factory aoDiCarFactory = new AoDiCarFactory(); Factory benChiCarFactory = new BenChiCarFactory(); //生產一輛紅旗汽車 Car hongQi = hongQiCarFactory.CreateCar(); hongQi.Go(); //生產一輛奧迪汽車 Car aoDi = aoDiCarFactory.CreateCar(); aoDi.Go(); //生產一輛賓士汽車 Car benChi = benChiCarFactory.CreateCar(); benChi.Go(); Console.Read(); #endregion } }View Code
運行結果如下:
三、Factory Method模式的幾個要點
Factory Method模式主要用於隔離類對象的使用者和具體類型之間的耦合關係。面對一個經常變化的具體類型,緊耦合關係會導致軟體的脆弱;
Factory Method模式通過面向對象的手法,將所要創建的具體對象工作延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦
合關係;
Factory Method模式解決“單個對象”的需求變化;
AbstractFactory模式解決“系列對象”的需求變化;
Builder模式解決“對象部分”的需求變化;
3.1、工廠方法模式的優點
1)在工廠方法中,用戶只需要知道所要產品的具體工廠,無須關心具體的創建過程,甚至不需要具體產品類的類名。
2)在系統增加新的產品時,我們只需要添加一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了“開閉原則”。
3.2工廠方法模式的缺點
1)每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加
了系統具體類的依賴,這並不是什麼好事。
3.3工廠方法模式的使用場合
1)一個類不知道它所需要的對象的類。在工廠方法模式中,我們不需要具體產品的類名,我們只需要知道創建它的具體工廠即可。
2)一個類通過其子類來指定創建那個對象。在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的介面,由其子類來確定具體要創建的對
象。在程式運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
3)將創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定。
四、.NET中實現了工廠方法的類
.NET類庫中也有很多實現了工廠方法的類。例如在Asp.net中,處理程式對象是具體用來處理請求,當我們請求一個*.aspx的文件時,此時會映射到
System.Web.UI.PageHandlerFactory類上進行處理,而對*.ashx的請求將映射到System.Web.UI.SimpleHandlerFactory類中(這兩個類都是繼承於
IHttpHandlerFactory介面的),關於這點說明我們可在“C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\Web.Config”文件中找到相關定義:
<add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True"/>
<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/>
<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True"/>
上面只摘選了部分配置文件,有時間大家可以自己去研究一下。
下麵我們具體看下工廠方法模式在Asp.net中是如何實現的?對一個Index.aspx頁面發出請求時,將會調用PageHandlerFactory中GetHandler方法來
創建一個Index.aspx對象,它們之間的類圖關係如下:
五、總結
每種模式都有自己的使用場合,切記,如果使用錯誤,還不如不用。工廠方法模式通過面向對象編程中的多態性來將對象的創建延遲到具體工廠中,
從而解決了簡單工廠模式中存在的問題,也很好地符合了開放封閉原則(即對擴展開發,對修改封閉)。
學習設計模式我們一定要謹記設計模式的幾大原則,否則是徒勞無功的。就像學務工一樣,我們要記心法。幾大原則就像獨孤九劍的劍訣,學會了,
變化無窮。