C#設計模式之二工廠方法模式(Factory Method Pattern)【創建型】


一、引言

       在上一篇文章中我們講解了過渡的一種模式叫做【簡單工廠】,也有叫【靜態工廠】的,通過對簡單工廠模式得了解,我們也發現了它的缺點,就是隨着需求的變化我們要不停地修改工廠里面的方法的代碼,需求變化越多,里面的If--Else--也越多,這樣就會造成簡單工廠的實現邏輯過於復雜。設計模式是遵循一定原則而得來的,比如,我們要怎么增加代碼,怎么修改代碼,不是想怎么來就怎么來的,其中一個原則就是OCP原則,中文是【開放關閉原則】,對增加代碼開發,對修改代碼關閉,所以我們就不能總是這樣修改簡單工廠里面的方法。本章介紹的工廠方法模式可以解決簡單工廠模式中存在的這個問題,下面就具體看看工廠方法模式是如何解決該問題的。


二、工廠方法模式的胡介紹

      2.1、動機(Motivate)

           在軟件系統的構建過程中,經常面臨着“某個對象”的創建工作:由於需求的變化,這個對象(的具體實現)經常面臨着劇烈的變化,但是它卻擁有比較穩定的接口

      如何應對這種變化?如何提供一種“封裝機制”來隔離出“這個易變對象”的變化,從而保持系統中“其他依賴對象的對象”不隨着需求改變而改變?

      2.2、意圖(Intent)

         
定義一個用於創建對象的接口,讓子類決定實例化哪一個類。Factory Method使得一個類的實例化延遲到子類。                                        --《設計模式》GoF

      2.3、結構圖(Structure)

          

     2.4、模式的組成

          可以看出,在工廠方法模式的結構圖有以下角色:

          (1)、抽象工廠角色(Creator):
充當抽象工廠角色,定義工廠類所具有的基本的操作,任何具體工廠都必須繼承該抽象類。

          (2)、具體工廠角色(ConcreteCreator):充當具體工廠角色,該類必須繼承抽象工廠角色,實現抽象工廠定義的方法,用來創建具體產品。

          (3)、抽象產品角色(Product):充當抽象產品角色,定義了產品類型所有具有的基本操作,具體產品必須繼承該抽象類。

          (4)、具體產品角色(ConcreteProduct):充當具體產品角色,實現抽象產品類對定義的抽象方法,由具體工廠類創建,它們之間有一一對應的關系。

     2.5、工廠方法模式的代碼實現

         【簡單工廠模式】的問題是:如果有新的需求就需要修改工廠類里面創建產品對象實例的那個方法的實現代碼,在面向對象設計一個原則就是哪里有變化,我就封裝哪里。還有另外兩個大的原則,其一是:面向抽象編程,細節和高層實現都要依賴抽象,第二個原則是:多組合,少繼承。這三個原則是最根本的原則,學習設計模式必須以這三個原則為基點,否則都是枉然。根據這三大原則又衍生出來6個具體的原則,分別是【單一職責原則】,【里氏替換原則】,【依賴倒置原則】,【接口隔離原則】、【迪米特法則】和【開閉原則,既然工廠類有變化,我們就封裝它,面向抽象編程,我們先抽象出一個工廠基類,然后,每個需求就實現一個具體的工廠類,這樣我們就符合了【開閉原則OCP】,讓一個工廠生產一款產品,並一一對應。我們把具體產品的創建推遲到子類中,此時工廠類(這類是基類了)不再負責所有產品的創建,而只是給出具體工廠必須實現的接口,這樣工廠方法模式就可以允許系統不修改工廠類邏輯的情況下來添加新產品,這樣也就克服了簡單工廠模式中缺點。下面就是工廠方法模式的實現代碼:

 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     


三、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對象,它們之間的類圖關系如下:



五、總結

       每種模式都有自己的使用場合,切記,如果使用錯誤,還不如不用。工廠方法模式通過面向對象編程中的多態性來將對象的創建延遲到具體工廠中,從而解決了簡單工廠模式中存在的問題,也很好地符合了開放封閉原則(即對擴展開發,對修改封閉)。

學習設計模式我們一定要謹記設計模式的幾大原則,否則是徒勞無功的。就像學務工一樣,我們要記心法。6大原則就像孤獨求敗的獨孤九劍的劍訣,學會了,變化無窮。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM