C#設計模式之五原型模式(Prototype Pattern)【建立型】

1、引言

      在開始今天的文章以前先說明一點,歡迎你們來指正。不少人說原型設計模式會節省機器內存,他們說是拷貝出來的對象,這些對象其實都是原型的複製,不會使用內存。我認爲這是不對的,由於拷貝出來的每個對象都是實際存在的,每一個對象都有本身的獨立內存地址,都會被GC回收。若是就淺拷貝來講,可能會公用一些字段,深拷貝是不會的,因此說原型設計模式會提升內存使用率,不必定。具體還要看當時的設計,若是拷貝出來的對象緩存了,每次使用的是緩存的拷貝對象,那就另當別論了,再說該模式自己解決的不是內存使用率的問題。
     如今說說原型模式的要解決的問題吧,在軟件系統中,當建立一個類的實例的過程很昂貴或很複雜,而且咱們須要建立多個這樣類的實例時,若是咱們用new操做符去建立這樣的類實例,這就會增長建立類的複雜度和建立過程與客戶代碼複雜的耦合度。若是採用工廠模式來建立這樣的實例對象的話,隨着產品類的不斷增長,致使子類的數量不斷增多,也致使了相應工廠類的增長,維護的代碼維度增長了,由於有產品和工廠兩個維度了,反而增長了系統複雜程度,因此在這裏使用工廠模式來封裝類建立過程並不合適。因爲每一個類實例都是相同的,這個相同指的是類型相同,可是每一個實例的狀態參數會有不一樣,若是狀態數值也相同就沒意義了,有一個這樣的對象就能夠了。當咱們須要多個相同的類實例時,能夠經過對原來對象拷貝一份來完成建立,這個思路正是原型模式的實現方式。

2、原型模式的詳細介紹

2.一、動機(Motivate)

        在軟件系統中,常常面臨着「某些結構複雜的對象」的建立工做;因爲需求的變化,這些對象常常面臨着劇烈的變化,可是它們卻擁有比較穩定一致的接口。如何應對這種變化?如何向「客戶程序(使用這些對象的程序)」隔離出「這些易變對象」,從而使得「依賴這些易變對象的客戶程序」不隨着需求改變而改變?

2.二、意圖(Intent)

      使用原型實例指定建立對象的種類,而後經過拷貝這些原型來建立新的對象。                 --《設計模式》Gof

2.三、結構圖(Structure)

     

2.四、模式的組成

      能夠看出,在原型模式的結構圖有如下角色:

     (1)、原型類(Prototype):原型類,聲明一個Clone自身的接口;

   (2)、具體原型類(ConcretePrototype):實現一個Clone自身的操做。

    在原型模式中,Prototype一般提供一個包含Clone方法的接口,具體的原型ConcretePrototype使用Clone方法完成對象的建立。

2.5 原型模式的具體實現

      《大話西遊之大聖娶親》這部電影,沒看過的人很少吧,裏面有這樣一個場景。牛魔王使用無敵牛蝨大戰至尊寶,至尊寶的應對之策就是,從腦後把下一撮猴毛,吹了口仙氣,無數猴子猴孫現身,來大戰牛魔王的無敵牛蝨。至尊寶的猴子猴孫就是該原型模式的最好體現。至尊寶建立本身的一個副本,不用還要從新孕育五百年,而後出世,再學藝,最後來和老牛大戰,估計黃花菜都涼了。他有3根救命猴毛,輕輕一吹,想要多少個本身就有多少個,方便,快捷。
  設計模式

 1 /// <summary>
 2 /// 原型設計模式,每一個具體原型是一類對象的原始對象,經過每一個原型對象克隆出來的對象也能夠進行設置,在原型的基礎之上豐富克隆出來的對象,因此要設計好抽象原型的接口
 3 /// </summary>
 4 namespace 設計模式之原型模式
 5 {
 6     /// <summary>
 7     /// 客戶類
 8     /// </summary>
 9     class Customer
10     {
11         static void Main(string[] args)
12         {
13             Prototype xingZheSun = new XingZheSunPrototype();
14             Prototype xingZheSun2 = xingZheSun.Clone();
15             Prototype xingZheSun3 = xingZheSun.Clone();
16 
17             Prototype sunXingZhe = new SunXingZhePrototype();
18             Prototype sunXingZhe2 = sunXingZhe.Clone();
19             Prototype sunXingZhe3 = sunXingZhe.Clone();
20             Prototype sunXingZhe4 = sunXingZhe.Clone();
21             Prototype sunXingZhe5 = sunXingZhe.Clone();
22 
23             //1號孫行者打妖怪
24             sunXingZhe.Fight();
25             //2號孫行者去化緣
26             sunXingZhe2.BegAlms();
27 
28             //戰鬥和化緣也能夠分類,好比化緣,能夠分:水果類化緣,飯食類化緣;戰鬥能夠分爲:天界寵物下界成妖的戰鬥,天然修煉成妖的戰鬥,你們能夠本身去想吧,原型模式仍是頗有用的
29 
30             Console.Read();
31         }
32     }
33 
34     /// <summary>
35     /// 抽象原型,定義了原型自己所具備特徵和動做,該類型就是至尊寶
36     /// </summary>
37     public abstract class Prototype
38     {
39         // 戰鬥--保護師傅
40         public abstract void Fight();
41         // 化緣--不要餓着師傅
42         public abstract void BegAlms();
43 
44         // 吹口仙氣--變化一個本身出來
45         public abstract Prototype Clone();
46     }
47 
48     /// <summary>
49     /// 具體原型,例如:行者孫,他只負責化齋飯食和與天界寵物下界的妖怪的戰鬥
50     /// </summary>
51     public sealed class XingZheSunPrototype:Prototype
52     {
53         // 戰鬥--保護師傅--與天然修煉成妖的戰鬥
54         public override void Fight()
55         {
56             Console.WriteLine("騰雲駕霧,各類武藝");
57         }
58         // 化緣--不要餓着師傅--飯食類
59         public override void BegAlms()
60         {
61             Console.WriteLine("什麼都能要來");
62         }
63 
64         // 吹口仙氣--變化一個本身出來
65         public override Prototype Clone()
66         {
67             return (XingZheSunPrototype)this.MemberwiseClone();
68         }
69     }
70 
71     /// <summary>
72     /// 具體原型,例如:孫行者,他只負責與天然界修煉成妖的戰鬥和化齋水果
73     /// </summary>
74     public sealed class SunXingZhePrototype : Prototype
75     {
76         // 戰鬥--保護師傅-與天界寵物戰鬥
77         public override void Fight()
78         {
79             Console.WriteLine("騰雲駕霧,各類武藝");
80         }
81         // 化緣--不要餓着師傅---水果類
82         public override void BegAlms()
83         {
84             Console.WriteLine("什麼都能要來");
85         }
86 
87         // 吹口仙氣--變化一個本身出來
88         public override Prototype Clone()
89         {
90             return (SunXingZhePrototype)this.MemberwiseClone();
91         }
92     }
93 }


   上面代碼中都有詳細的註釋代碼,這裏就不過多解釋。

3、原型模式的實現要點: 
 
       Prototype模式一樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它一樣要求這些「易變類」擁有「穩定的接口」。

  Prototype模式對於「如何建立易變類的實體對象」(建立型模式除了Singleton模式之外,都是用於解決建立易變類的實體對象的問題的)採用「原型克隆」的方法來作,它使得咱們能夠很是靈活地動態建立「擁有某些穩定接口」的新對象——所需工做僅僅是註冊一個新類的對象(即原型),而後在任何須要的地方不斷地Clone。

  Prototype模式中的Clone方法能夠利用.NET中的Object類的MemberwiseClone()方法或者序列化來實現深拷貝。

    3.1】、原型模式的優勢:

        (1)、原型模式向客戶隱藏了建立新實例的複雜性

        (2)、原型模式容許動態增長或較少產品類。

        (3)、原型模式簡化了實例的建立結構,工廠方法模式須要有一個與產品類等級結構相同的等級結構,而原型模式不須要這樣。

        (4)、產品類不須要事先肯定產品的等級結構,由於原型模式適用於任何的等級結構

   3.2】、原型模式的缺點:

       (1)、每一個類必須配備一個克隆方法

       (2)、配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。

   3.3】、原型模式使用的場景:

          (1)、資源優化場景

                      類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等。

         (2)、性能和安全要求的場景

                    經過new產生一個對象須要很是繁瑣的數據準備或訪問權限,則可使用原型模式。

         (3)、一個對象多個修改者的場景

                    一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值時,能夠考慮使用原型模式拷貝多個對象供調用者使用。

           在實際項目中,原型模式不多單獨出現,通常是和工廠方法模式一塊兒出現,經過clone的方法建立一個對象,而後由工廠方法提供給調用者。

4、.NET 中原型模式的實現

      在.NET中,微軟已經爲咱們提供了原型模式的接口實現,該接口就是ICloneable,其實這個接口就是抽象原型,提供克隆方法,至關於與上面代碼中Prototype抽象類,其中的Clone()方法來實現原型模式,若是咱們想咱們自定義的類具備克隆的功能,首先定義類實現ICloneable接口的Clone方法。其實在.NET中實現了ICloneable接口的類有不少,以下圖所示(圖中只截取了部分,能夠用ILSpy反編譯工具進行查看):緩存

namespace System
{
    [ComVisible(true)]
    public interface ICloneable
    {
        object Clone();
    }
}

在Net的FCL裏面實現ICloneable接口的類如圖,本身能夠去查看每一個類本身的實現,在此就不貼出來了。安全



5、總結

       到今天爲止,全部的建立型設計模式就寫完了。學習設計模式應該是有一個按部就班的過程,當咱們寫代碼的時候不要一上來就用什麼設計模式,而是經過重構來使用設計模式。建立型的設計模式寫完了,咱們就總結一下。Singleton單件模式解決的是實體對象個數的問題。除了Singleton以外,其餘建立型模式解決的都是new所帶來的耦合關係。 Factory Method,Abstract Factory,Builder都須要一個額外的工廠類來負責實例化「易變對象」,而Prototype則是經過原型(一個特殊的工廠類)來克隆「易變對象」。(其實原型就是一個特殊的工廠類,它只是把工廠和實體對象耦合在一塊兒了)。若是遇到「易變類」,起初的設計一般從Factory Method開始,當遇到更多的複雜變化時,再考慮重構爲其餘三種工廠模式(Abstract Factory,Builder,Prototype)。
 通常來講,若是可使用Factory Method,那麼必定可使用Prototype。可是Prototype的使用狀況通常是在類比較容易克隆的條件之上,若是是每一個類實現比較簡單,均可以只用實現MemberwiseClone,沒有引用類型的深拷貝,那麼就更適合了。ide

相關文章
相關標籤/搜索