設計模式目錄:html
本篇目錄:編程
概述設計模式
實現ide
深拷貝(Deep Copy)post
其實說到原型模式,你們可能會想到Clone,其實也不盡然,在咱們的平常生活中,原型(Prototype)模式也能夠常常看到:學習
你以爲某位明星的髮型很好看,你也想作個和他同樣的髮型,因而你拿個照片去理髮店說:我要作個和這個如出一轍的髮型。測試
其實那個明星的髮型某種意義上說就是原型,由於已經有髮型參考了,因此髮型師會很快的作出來。就像咱們在編程時,當建立一些對象(特別是大對象)很是耗時時,或者建立過程很是複雜時,原型模式就會頗有用。this
咱們看下GoF對原型(Prototype)模式下的定義:url
使用原型模型實例指定將要建立的對象類型,經過複製這個實例建立新的對象。spa
從這個定義中,咱們能夠分解下,其實說的意思就是:指定類型,複製對象。就像上面的例子,造型就是一個指定的類型,而後去複製此類型的對象。原型模型的簡單靜態類圖:
Client使用Prototype的Clone()方法獲得這個對象的副本,咱們先看下引用類型的存儲:引用類型的變量在棧中分配一個內存空間,這個內存空間包含的是對另外一個內存位置的引用,這個位置是託管堆中的一個地址,即存放此變量實際值的地方。.NET自動維護一個堆指針,它包含堆中下一個可用內存空間的地址。就是說引用類型在棧中存儲一個託管堆的地址,託管堆存儲這個變量的實際值。上面所說的「複製對象」在原型(Prototype)模式中分爲兩種,一種是隻複製棧中的託管堆地址,就是說副本對象和原對象指向的託管堆的地址是同樣的,這種複製稱爲淺拷貝(Shallow Copy);另外一種是棧中存放的地址和託管堆中實際值都複製,就是說生成一個和原對象同樣的全新對象,這種複製稱爲深拷貝(Deep Copy)。
咱們用一張示意圖能夠簡單概述下:
其實關於淺拷貝和深拷貝在咱們現實生活中也有相應相似的例子,好比雙胞胎,長的同樣就像一個模子刻出來的同樣,並且各自有各自的身體互不影響,在某種意義上說能夠稱爲深拷貝;在雙胞胎中也有一些特殊的,好比連體雙胞胎,有的兩個身體公用一個心臟什麼的,怎麼說呢,在某種意義上這種你也能夠稱爲淺拷貝。這個例子只是形象說明下原型模式中複製對象的含義,可能有些不恰當的地方。
其實在.net中關於淺拷貝提供了一個方法MemberwiseClone();
MemberwiseClone()方法返回的是Object類型,注意方法的修飾符是protected,就是說,若是想讓外部對象使用它,必須在子類重寫該方法,設定其訪問範圍是public。還有就是實現對象的複製必須實現ICloneable接口,並實現其Clone()方法。就上面說的雙胞胎的例子,咱們作一個簡單的示例:
1 /// <summary> 2 /// 心臟類 3 /// </summary> 4 public class Heart 5 { 6 private int _size; 7 private int _volume; 8 /// <summary> 9 /// 大小 10 /// </summary> 11 public int Size 12 { 13 get { return _size; } 14 set { _size = value; } 15 } 16 /// <summary> 17 /// 體積 18 /// </summary> 19 public int Volume 20 { 21 get { return _volume; } 22 set { _volume = value; } 23 } 24 } 25 26 /// <summary> 27 /// baby類 28 /// </summary> 29 public class Baby : ICloneable 30 { 31 private string _name; 32 private string _description; 33 private Heart _hearttype; 34 /// <summary> 35 /// 名稱 36 /// </summary> 37 public string Name 38 { 39 get { return _name; } 40 set { _name = value; } 41 } 42 /// <summary> 43 /// 描述 44 /// </summary> 45 public string Description 46 { 47 get { return _description; } 48 set { _description = value; } 49 } 50 /// <summary> 51 /// 心臟特徵 52 /// </summary> 53 public Heart HeartType 54 { 55 get { return _hearttype; } 56 set { _hearttype = value; } 57 } 58 59 #region ICloneable 成員 60 public object Clone() 61 { 62 return this.MemberwiseClone(); 63 } 64 #endregion 65 }
測試代碼:
1 static void Main(string[] args) 2 { 3 Baby baby1 = new Baby(); 4 baby1.Name = "I'm baby1"; 5 baby1.Description = "I'm baby"; 6 baby1.HeartType = new Heart() { Size = 111, Volume = 222 }; 7 Baby baby2 = (Baby)baby1.Clone(); 8 baby2.Name = "I'm baby2"; 9 10 Console.WriteLine("baby1 info:"); 11 Console.WriteLine(baby1.Name); 12 Console.WriteLine(baby1.Description); 13 Console.WriteLine("baby2 info:"); 14 Console.WriteLine(baby2.Name); 15 Console.WriteLine(baby2.Description); 16 Console.WriteLine("The heart of the different:"); 17 Console.WriteLine(baby1.HeartType == baby2.HeartType); 18 }
運行結果:
咱們能夠看到baby1.HeartType和baby2.HeartType的引用地址是同樣的,這種就是淺拷貝(Shallow Copy),就說明baby1和baby2公用一個心臟,是連體雙胞胎。
其實上面的代碼稍微修改下就是深拷貝,以下:
1 static void Main(string[] args) 2 { 3 Baby baby1 = new Baby(); 4 baby1.Name = "I'm baby1"; 5 baby1.Description = "I'm baby"; 6 baby1.HeartType = new Heart() { Size = 111, Volume = 222 }; 7 Baby baby2 = (Baby)baby1.Clone(); 8 baby2.HeartType = new Heart() { Size = 111, Volume = 222 };//從新建立對象 9 baby2.Name = "I'm baby2"; 10 11 Console.WriteLine("baby1 info:"); 12 Console.WriteLine(baby1.Name); 13 Console.WriteLine(baby1.Description); 14 Console.WriteLine("baby2 info:"); 15 Console.WriteLine(baby2.Name); 16 Console.WriteLine(baby2.Description); 17 Console.WriteLine("The heart of the different:"); 18 Console.WriteLine(baby1.HeartType == baby2.HeartType); 19 }
上面給baby2從新建立一個和baby1同樣的心臟,而不是公用一個,運行結果:
能夠看到baby1.HeartType和baby2.HeartType的引用地址是不同的,雖然是同樣的心臟,可是是兩個獨立相同的心臟,其實上面的方法並不算是深拷貝,只是實現了深拷貝的效果,由於並非在拷貝中完成的。這種氣勢有個很差的地方,當一個對象中有不少對象組合的時候,並且這個對象內部很複雜,咱們不可能複製完以後,每一個對象的去從新賦值,這樣實現深拷貝就沒有什麼意義。固然還有一種實現深拷貝的方式就是序列化,必須在類的前面加上[Serializable]表示,指示這個類是能夠序列化的,咱們把Clone()的方法修改下:
1 public object Clone() 2 { 3 object result = null; 4 MemoryStream stream = new MemoryStream(); 5 BinaryFormatter formatter = new BinaryFormatter(); 6 formatter.Serialize(stream, this); 7 stream.Close(); 8 byte[] streamByte = stream.ToArray(); 9 MemoryStream stream2 = new MemoryStream(streamByte); 10 result = formatter.Deserialize(stream2); 11 stream2.Close(); 12 return result; 13 }
如今Clone()方法作的工做就是序列化和反序列化,咱們使用淺拷貝的測試代碼,運行結果爲:
baby1.HeartType和baby2.HeartType的引用地址是不同的,和上面從新賦值對象的效果是同樣的,可是咱們調用的時候沒有作額外的操做,就能夠實現此效果,可是序列化和反序列化是比較耗時的,這點也須要注意下。
示例代碼下載:Prototype.rar
關於建立型模式上面幾篇說的差很少,還有個針對工廠方法模式出現問題的解決方案,下面就是結構型模式了,還在學習中,未完待續。。。