本筆記摘抄自:http://www.javashuo.com/article/p-ktjdqwpc-gh.html,記錄一下學習過程以備後續查用。html
1、引言設計模式
不少人說原型設計模式會節省機器內存,他們說是拷貝出來的對象是原型的複製,不會使用內存。我認爲這是不對的,由於拷貝出來的每個對象都是實際緩存
存在的,每一個對象都有本身獨立的內存地址且會被GC回收。若是就淺拷貝來講,可能會公用一些字段(引用類型),但深拷貝是不會的。因此說原型設計模式會安全
提升內存使用率是不必定的,具體還要看當時的設計,若是拷貝出來的對象緩存了,每次使用的是緩存的拷貝對象,那就另當別論,再說該模式自己解決的不ide
是內存使用率的問題。工具
附:淺複製與深複製的區別
淺複製一個對象:
1)若是這個對象(如int age=18 )是值類型,則獲得的對象是一個全新的值類型對象(新的內存地址);
2)若是這個對象是引用類型(如class Person):
I、這個對象中的值類型(如person.Age=18)是一個全新的值類型對象(新的內存地址);
II、這個對象中的引用類型是公用的(同一內存地址),當原始引用類型的值變化時,新生成對象的引用類型的值也會跟着變化。
深複製一個對象:
不管以前這個對象是值類型仍是引用類型,獲得的新對象都是一個全新的對象(新的內存地址)。
在軟件系統中,當建立一個類的實例的過程很昂貴或很複雜,而且咱們須要建立多個這樣的類的實例時,若是用new操做符去建立時,會增長建立的複雜度性能
與客戶代碼的耦合度。若是採用工廠方法模式來建立這樣的實例對象的話,隨着產品類的不斷增長,致使子類的數量不斷增多,也致使了相應工廠類的增長,學習
系統複雜程度隨之增長,因此此時使用工廠方法模式來封裝類的建立過程並不合適。優化
因爲每一個類的實例都是相同的(這個相同指的是類型相同,可是每一個實例的狀態參數會有不一樣,若是狀態數值也相同就沒意義了),有一個這樣的對象就可spa
以了。當咱們須要多個相同的類實例時,能夠經過對原來對象拷貝一份來完成建立,這個思路正是原型模式的實現方式。
2、原型模式介紹
原型模式:英文名稱--Prototype Pattern;分類--建立型。
2.一、動機(Motivate)
在軟件系統中,常常面臨着「某些結構複雜的對象」的建立工做,因爲需求的變化,這些對象常常面臨着劇烈的變化,可是它們卻擁有比較穩定一致的接口。
如何應對這種變化?如何向「客戶程序(使用這些對象的程序)」隔離出「這些易變對象」,從而使得「依賴這些易變對象的客戶程序」不隨着需求改變而改變?
2.二、意圖(Intent)
使用原型實例指定建立對象的種類,而後經過拷貝這些原型來建立新的對象。--《設計模式》Gof
2.三、結構圖(Structure)
2.四、模式的組成
從上圖能夠看出,在原型模式的結構圖有如下角色:
1)原型類(Prototype):原型類,聲明一個Clone自身的接口。
2)具體原型類(ConcretePrototype):實現一個Clone自身的操做。
在原型模式中,Prototype一般提供一個包含Clone方法的接口,具體的原型ConcretePrototype使用Clone方法完成對象的建立。
2.五、原型模式的具體實現
《大話西遊之大聖娶親》這部電影,裏面有這樣一個場景:牛魔王使用無敵牛蝨大戰至尊寶,至尊寶的應對之策就是--從腦後拔下一撮猴毛,吹了口仙氣,
無數猴子猴孫現身來大戰牛魔王的無敵牛蝨。至尊寶的猴子猴孫就是該原型模式的最好體現,至尊寶建立本身的一個副本,不用還要從新孕育五百年,而後出
世、再學藝,最後再來和老牛大戰,假如這樣的話,估計黃花菜都涼了。至尊寶有3根救命猴毛,輕輕一吹,想要多少個本身就有多少個,方便、快捷。
class Program { /// <summary> /// 抽象原型,定義了原型自己所具備特徵和動做,該類型就是至尊寶。 /// </summary> public abstract class Prototype { //戰鬥--保護師傅 public abstract void Fight(); //化緣--不要餓着師傅 public abstract void BegAlms(); //吹口仙氣--變一個本身出來 public abstract Prototype Clone(); } /// <summary> /// 具體原型,例如:行者孫A,他只負責與從天界寵物下界的妖怪戰鬥和化緣齋飯食。 /// </summary> public sealed class MonkeyKingPrototype : Prototype { //戰鬥--保護師傅 public override void Fight() { Console.WriteLine("七十二變,集萬千武藝於一身。"); } //化緣--不要餓着師傅 public override void BegAlms() { Console.WriteLine("阿彌陀佛!施主,請施捨點飯食。"); } //吹口仙氣--變一個本身出來 public override Prototype Clone() { return (MonkeyKingPrototype)MemberwiseClone(); } } /// <summary> /// 具體原型,例如:孫行者B,他只負責與天然界修煉成妖的妖怪戰鬥和化緣水果。 /// </summary> public sealed class NewskyPrototype : Prototype { //戰鬥--保護師傅 public override void Fight() { Console.WriteLine("七十二變,集萬千武藝於一身。"); } //化緣--不要餓着師傅 public override void BegAlms() { Console.WriteLine("阿彌陀佛!施主,請施捨點水果。"); } //吹口仙氣--變一個本身出來 public override Prototype Clone() { return (NewskyPrototype)MemberwiseClone(); } } static void Main(string[] args) { #region 原型模式 Prototype monkeyKing = new MonkeyKingPrototype(); Prototype monkeyKing1 = monkeyKing.Clone(); Prototype monkeyKing2 = monkeyKing.Clone(); Prototype newsky = new NewskyPrototype(); Prototype newsky1 = newsky.Clone(); Prototype newsky2 = newsky.Clone(); //孫行者A打妖怪 monkeyKing1.Fight(); //孫行者B去化緣 newsky2.BegAlms(); Console.Read(); #endregion } }
運行結果以下:
3、原型模式的實現要點
Prototype模式一樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它一樣要求這些「易變類」擁有「穩定的接口」。
Prototype模式對於「如何建立易變類的實體對象」(建立型模式除了Singleton模式之外,都是用於解決建立易變類的實體對象的問題的)採用「原型克隆」的方
法來作,它使得咱們能夠很是靈活地動態建立「擁有某些穩定接口」的新對象——所需工做僅僅是註冊一個新類的對象(即原型),而後在任何須要的地方不斷
地Clone。
Prototype模式中的Clone方法能夠利用.NET中的Object類的MemberwiseClone()方法或者序列化來實現深拷貝。
3.一、原型模式的優勢
1)原型模式向客戶隱藏了建立新實例的複雜性。
2)原型模式容許動態增長或較少產品類。
3)原型模式簡化了實例的建立結構,工廠方法模式須要有一個與產品類等級結構相同的等級結構,而原型模式不須要這樣。
4)產品類不須要事先肯定產品的等級結構,由於原型模式適用於任何的等級結構。
3.二、原型模式的缺點
1)每一個類必須配備一個克隆方法。
2)配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或
者引用含有循環結構的時候。
3.三、原型模式使用的場景
1)資源優化場景
類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等。
2)性能和安全要求的場景
經過new產生一個對象須要很是繁瑣的數據準備或訪問權限時,則可使用原型模式。
3)一個對象多個修改者的場景
一個對象須要提供給其它對象訪問並且各個調用者可能都須要修改其值時,能夠考慮使用原型模式拷貝多個對象供調用者使用。在實際項目中,原型模式不多
單獨出現,通常是和工廠方法模式一塊兒出現,經過clone的方法建立一個對象,而後由工廠方法提供給調用者。
4、.NET中原型模式的實現
在.NET中,微軟已經爲咱們提供了原型模式的接口實現,該接口就是ICloneable。其實這個接口就是抽象原型,提供克隆方法,至關於與上面代碼中Prototype
抽象類,其中的Clone()方法實現原型模式。若是想自定義的類具備克隆的功能,首先須要在類定義時實現ICloneable接口的Clone方法。
namespace System { [ComVisible(true)] public interface ICloneable { object Clone(); } }
其實在.NET中實現了ICloneable接口的類有不少,以下圖所示(只截取了部分,能夠用ILSpy反編譯工具進行查看):
5、總結
到本篇爲止,全部的建立型設計模式就寫完了。學習設計模式應該是一個按部就班的過程,當咱們寫代碼的時候不要一上來就用什麼設計模式,而是經過重構
來使用設計模式。
下面總結一下建立型的設計模式:
單例模式解決的是實體對象個數的問題。除了單例模式以外,其它的建立型模式解決的都是new所帶來的耦合關係。工廠方法模式、抽象工廠模式、建造者模
式都須要一個額外的工廠類來負責實例化「易變對象」,而原型模式則是經過原型(一個特殊的工廠類把工廠和實體對象耦合在一塊兒了)來克隆「易變對象」。若是
遇到「易變類」,起初的設計一般從工廠方法模式開始,當遇到更多的複雜變化時,再考慮重構爲其餘三種工廠模式(抽象工廠模式、建造者模式、原型模式)。
通常來講,若是可使用工廠方法模式,那麼必定可使用原型模式,可是原型模式的使用狀況通常是在類比較容易克隆的條件之上。若是是每一個類的實現都
比較簡單,只須要實現MemberwiseClone而沒有引用類型的深拷貝,那麼就更加適合了。