1、引言
在上一篇文章中咱們講解了過渡的一種模式叫作【簡單工廠】,也有叫【靜態工廠】的,經過對簡單工廠模式得了解,咱們也發現了它的缺點,就是隨着需求的變化咱們要不停地修改工廠裏面的方法的代碼,需求變化越多,裏面的If--Else--也越多,這樣就會形成簡單工廠的實現邏輯過於複雜。設計模式是遵循必定原則而得來的,好比,咱們要怎麼增長代碼,怎麼修改代碼,不是想怎麼來就怎麼來的,其中一個原則就是OCP原則,中文是【開放關閉原則】,對增長代碼開發,對修改代碼關閉,因此咱們就不能老是這樣修改簡單工廠裏面的方法。本章介紹的工廠方法模式能夠解決簡單工廠模式中存在的這個問題,下面就具體看看工廠方法模式是如何解決該問題的。編程
2、工廠方法模式的胡介紹
2.一、動機(Motivate)
在軟件系統的構建過程當中,常常面臨着「某個對象」的建立工做:因爲需求的變化,這個對象(的具體實現)常常面臨着劇烈的變化,可是它卻擁有比較穩定的接口。設計模式
如何應對這種變化?如何提供一種「封裝機制」來隔離出「這個易變對象」的變化,從而保持系統中「其餘依賴對象的對象」不隨着需求改變而改變?
2.二、意圖(Intent)
定義一個用於建立對象的接口,讓子類決定實例化哪個類。Factory Method使得一個類的實例化延遲到子類。 --《設計模式》GoF
2.三、結構圖(Structure)
2.四、模式的組成
能夠看出,在工廠方法模式的結構圖有如下角色:
(1)、抽象工廠角色(Creator): 充當抽象工廠角色,定義工廠類所具備的基本的操做,任何具體工廠都必須繼承該抽象類。
(2)、具體工廠角色(ConcreteCreator):充當具體工廠角色,該類必須繼承抽象工廠角色,實現抽象工廠定義的方法,用來建立具體產品。
(3)、抽象產品角色(Product):充當抽象產品角色,定義了產品類型全部具備的基本操做,具體產品必須繼承該抽象類。
(4)、具體產品角色(ConcreteProduct):充當具體產品角色,實現抽象產品類對定義的抽象方法,由具體工廠類建立,它們之間有一一對應的關係。
2.五、工廠方法模式的代碼實現
【簡單工廠模式】的問題是:若是有新的需求就須要修改工廠類裏面建立產品對象實例的那個方法的實現代碼,在面向對象設計一個原則就是哪裏有變化,我就封裝哪裏。還有另外兩個大的原則,其一是:面向抽象編程,細節和高層實現都要依賴抽象,第二個原則是:多組合,少繼承。這三個原則是最根本的原則,學習設計模式必須以這三個原則爲基點,不然都是枉然。根據這三大原則又衍生出來6個具體的原則,分別是【單一職責原則】,【里氏替換原則】,【依賴倒置原則】,【接口隔離原則】、【迪米特法則】和【開閉原則】,既然工廠類有變化,咱們就封裝它,面向抽象編程,咱們先抽象出一個工廠基類,而後,每一個需求就實現一個具體的工廠類,這樣咱們就符合了【開閉原則OCP】,讓一個工廠生產一款產品,並一一對應。咱們把具體產品的建立推遲到子類中,此時工廠類(這類是基類了)再也不負責全部產品的建立,而只是給出具體工廠必須實現的接口,這樣工廠方法模式就能夠容許系統不修改工廠類邏輯的狀況下來添加新產品,這樣也就克服了簡單工廠模式中缺點。下面就是工廠方法模式的實現代碼:ide
2 namespace 設計模式之工廠方法模式 3 { 4 /// <summary> 5 /// 汽車抽象類 6 /// </summary> 7 public abstract class Car 8 { 9 // 開始行駛 10 public abstract void Go(); 11 } 12 13 /// <summary> 14 /// 紅旗汽車 15 /// </summary> 16 public class HongQiCar : Car 17 { 18 public override void Go() 19 { 20 Console.WriteLine("紅旗汽車開始行駛了!"); 21 } 22 } 23 24 /// <summary> 25 /// 奧迪汽車 26 /// </summary> 27 public class AoDiCar : Car 28 { 29 public override void Go() 30 { 31 Console.WriteLine("奧迪汽車開始行駛了"); 32 } 33 } 34 35 /// <summary> 36 /// 抽象工廠類 37 /// </summary> 38 public abstract class Factory 39 { 40 // 工廠方法 41 public abstract Car CreateCar(); 42 } 43 44 /// <summary> 45 /// 紅旗汽車工廠類 46 /// </summary> 47 public class HongQiCarFactory:Factory 48 { 49 /// <summary> 50 /// 負責生產紅旗汽車 51 /// </summary> 52 /// <returns></returns> 53 public override Car CreateCar() 54 { 55 return new HongQiCar(); 56 } 57 } 58 59 /// <summary> 60 /// 奧迪汽車工廠類 61 /// </summary> 62 public class AoDiCarFactory:Factory 63 { 64 /// <summary> 65 /// 負責建立奧迪汽車 66 /// </summary> 67 /// <returns></returns> 68 public override Car CreateCar() 69 { 70 return new AoDiCar(); 71 } 72 } 73 74 /// <summary> 75 /// 客戶端調用 76 /// </summary> 77 class Client 78 { 79 static void Main(string[] args) 80 { 81 // 初始化建立汽車的兩個工廠 82 Factory hongQiCarFactory = new HongQiCarFactory(); 83 Factory aoDiCarFactory = new AoDiCarFactory(); 84 85 // 生產一輛紅旗汽車 86 Car hongQi = hongQiCarFactory.CreateCar(); 87 hongQi.Go(); 88 89 //生產一輛奧迪汽車 90 Car aoDi = aoDiCarFactory.CreateCar(); 91 aoDi.Go(); 92 93 Console.Read(); 94 } 95 } 96 }
在【工廠方法模式】中,咱們一樣也把汽車的類抽象出來一個抽象的基類,這裏正好符合了【面向抽象編程】,客戶端在使用的時候不會依賴具體的什麼汽車。使用工廠方法實現的系統,若是系統須要添加新產品時,咱們能夠利用多態性來完成系統的擴展,對於抽象工廠類和具體工廠中的代碼都不須要作任何改動。例如,咱們想增長一輛奔馳車,咱們只需從Car抽象類下繼承一個BenChiCar類,同時在從Factory抽象基類下繼承一個「奔馳」的工廠類BenChinaCarFactory就能夠了,這樣擴展符合OCP的原則。具體代碼爲:學習
1 /// <summary> 2 /// 奔馳車 3 /// </summary> 4 public class BenChiCar : Car 5 { 6 /// <summary> 7 /// 重寫抽象類中的方法 8 /// </summary> 9 public override void Go() 10 { 11 Console.WriteLine("奔馳車開始行駛了!"); 12 } 13 } 14 15 /// <summary> 16 /// 奔馳車的工廠類 17 /// </summary> 18 public class BenChiCarFactory : Factory 19 { 20 /// <summary> 21 /// 負責生產奔馳車 22 /// </summary> 23 /// <returns></returns> 24 public override Car CreateCar() 25 { 26 return new BenChiCar(); 27 } 28 } 29 30 /// <summary> 31 /// 客戶端調用 32 /// </summary> 33 class Client 34 { 35 static void Main(string[] args) 36 { 37 38 // 若是客戶又生產一輛奔馳車 39 // 再另外初始化一個奔馳車的工廠 40 Factory benChiCarFactory = new BenChiCarFactory(); 41 42 // 利用奔馳車的工廠生產奔馳車 43 Car benChi = benChiCarFactory.CreateCar(); 44 benChi.Go(); 45 46 Console.Read(); 47 } 48 } 49
3、Factory Method模式的幾個要點ui
Factory Method模式主要用於隔離類對象的使用者和具體類型之間的耦合關係。面對一個常常變化的具體類型,緊耦合關係會致使軟件的脆弱。spa
Factory Method模式經過面向對象的手法,將所要建立的具體對象工做延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關係。
.net
Factory Method模式解決「單個對象」的需求變化;設計
AbstractFactory模式解決「系列對象」的需求變化;code
Builder模式解決「對象部分」的需求變化;
3.1】、工廠方法模式的優勢:
(1)、 在工廠方法中,用戶只須要知道所要產品的具體工廠,無須關係具體的建立過程,甚至不須要具體產品類的類名。
(2)、在系統增長新的產品時,咱們只須要添加一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了「開閉原則」。
3.2】、工廠方法模式的缺點:
(1)、每次增長一個產品時,都須要增長一個具體類和對象實現工廠,是的系統中類的個數成倍增長,在必定程度上增長了系統的複雜度,同時也增長了系統具體類的依賴。這並非什麼好事。
3.3】、工廠方法模式使用的場景:
(1)、一個類不知道它所須要的對象的類。在工廠方法模式中,咱們不須要具體產品的類名,咱們只須要知道建立它的具體工廠便可。
(2)、一個類經過其子類來指定建立那個對象。在工廠方法模式中,對於抽象工廠類只須要提供一個建立產品的接口,而由其子類來肯定具體要建立的對象,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。
(3)、將建立對象的任務委託給多個工廠子類中的某一個,客戶端在使用時能夠無須關心是哪個工廠子類建立產品子類,須要時再動態指定。
4、.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對象,它們之間的類圖關係以下:
5、總結
每種模式都有本身的使用場合,切記,若是使用錯誤,還不如不用。工廠方法模式經過面向對象編程中的多態性來將對象的建立延遲到具體工廠中,從而解決了簡單工廠模式中存在的問題,也很好地符合了開放封閉原則(即對擴展開發,對修改封閉)。
學習設計模式咱們必定要謹記設計模式的幾大原則,不然是徒勞無功的。就像學務工同樣,咱們要記心法。6大原則就像孤獨求敗的獨孤九劍的劍訣,學會了,變化多端。