设计模式目录: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
关于建立型模式上面几篇说的差很少,还有个针对工厂方法模式出现问题的解决方案,下面就是结构型模式了,还在学习中,未完待续。。。