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