设计模式之原型模式(Prototype)
1、出现原因
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象” ,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
2、意图:
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。(将相同的部分进行赋值拷贝,不同的部分需要客户端自己决定)
3、结构图
4、代码演示
举例:比如楼房的基本信息是相同的,但是他们的门牌号码是不同的,需要客户自己决定的。
1 //原型接口 2 public interface IBuildingPrototype 3 { 4 string BuildingName { set; get; } 5 string BuildingNo { set; get; } 6 IBuildingPrototype Clone(); 7 } 8 //实现原型接口的类 9 public class Building : IBuildingPrototype 10 { 11 public string BuildingName 12 { 13 get; 14 set; 15 } 16 17 public string BuildingNo 18 { 19 get; 20 set; 21 } 22 23 public IBuildingPrototype Clone() 24 { 25 Building building = new Building(); 26 building.BuildingName = this.BuildingName; 27 return building; 28 } 29 }
客户端调用代码:
1 Building b1 = new Building(); 2 b1.BuildingName = "小别墅"; 3 b1.BuildingNo = "B1203"; 4 IBuildingPrototype b2 = b1.Clone(); 5 b2.BuildingNo = "B1230"; 6 Console.WriteLine(b2.BuildingName+":"+b2.BuildingNo);
使用.Net内部的原型模式的ICloneable接口实现版本
1 public class BuildingIclone : ICloneable 2 { 3 public string BuildingName { set; get; } 4 public string BildingNo { get; set; } 5 public object Clone() 6 { 7 BuildingIclone building = new BuildingIclone(); 8 building.BuildingName = this.BuildingName; 9 return building; 10 } 11 }
5、深拷贝和浅拷贝
提到拷贝:就要想到深拷贝和浅拷贝,下面就来讲解他们之间的区别:
他们之间的区别是体现在他们之中的 引用类型的 属性的 拷贝方式,其他值类型和string类型的属性只是直接拷贝数值:
浅拷贝:只是拷贝 引用类型的引用指针,新拷贝出来的实例的此属性还是指向原来的实例的属性的“地址”。
深拷贝:是将引用类型的属性 从新在内存中 创建一份 然后赋值给新拷贝到实例对应的属性。
在.Net中实现浅拷贝的方式Object.MemberwiseClone();
实现深拷贝要自己写代码了,实现的方式有两种:1)将浅拷贝来的实例的引用类型的属性在从新赋值一个新的从内存中创建的实例。
2)使用序列化的方式实现深拷贝(将浅拷贝的实例进行序列化,然后再反序列化得到深拷贝的实例)
代码来演示:
1 //下面演示 :浅拷贝和深拷贝的区别 2 [Serializable] 3 public class WorkExperience : ICloneable 4 { 5 public string CompanyName { get; set; } 6 7 public int Time { get; set; } 8 9 public object Clone() 10 { 11 return this.MemberwiseClone(); 12 } 13 14 public override string ToString() 15 { 16 return string.Format("在{0}公司工作了{1}年",CompanyName,Time); 17 } 18 } 19 20 [Serializable] 21 public class Worker : ICloneable 22 { 23 public string Name { get; set; } 24 25 public int Age { get; set; } 26 27 public WorkExperience WorkExprien { set; get; } 28 29 //浅拷贝 30 public Worker QianClone() 31 { 32 return (Worker)this.MemberwiseClone(); 33 } 34 35 //深拷贝(将浅拷贝的实例的引用类型属性从新赋值的方式) 36 public object Clone() 37 { 38 Worker worker = (Worker)this.MemberwiseClone(); 39 worker.WorkExprien = (WorkExperience)this.WorkExprien.Clone(); 40 return worker; 41 } 42 43 //深拷贝(序列化版本),要将要被序列化的类打上Serializable的特性 44 public Worker ShenClone() 45 { 46 Worker worker = (Worker) this.MemberwiseClone(); 47 BinaryFormatter binary = new BinaryFormatter(); 48 using (Stream stream = new MemoryStream()) 49 { 50 binary.Serialize(stream, this); 51 stream.Position = 0;//将当前流 重新定位,,否则会报错:“在分析完成之前就遇到流结尾。” 52 return (Worker) binary.Deserialize(stream); 53 } 54 } 55 56 }
测试代码:
1 Worker worker = new Worker() 2 { 3 Name = "小明", 4 Age = 23, 5 WorkExprien = new WorkExperience() { CompanyName="百度",Time=2} 6 }; 7 8 Worker w1 = worker.QianClone();//浅拷贝 9 Worker w2 = (Worker)worker.Clone();//深拷贝 10 Worker w3 = worker.ShenClone();//序列化版本的深拷贝 11 Console.WriteLine(worker.WorkExprien.Equals(w1.WorkExprien));//True 12 Console.WriteLine(worker.WorkExprien.Equals(w2.WorkExprien));//False 13 Console.WriteLine(worker.WorkExprien.Equals(w3.WorkExprien));//False
6、.Net中的原型模式
1》就是上面提到的ICloneable接口
2》DataSet
Clone()方法:浅拷贝:只复制表的结构,不复制表的数据
Copy()方法:深拷贝:既复制表的结构,又复制表的数据
7、实现要点
1、产品的创建和初始化在类的Clone方法中完成
2、浅复制与深复制的区别
3、需要深复制的场合需要开发人员根据需要实现(在重写的 Clone 里 将引用类型 成员进行复制)
4、有些情况下Clone功能不容易实现,特别是遇到对象的循环引用时(例如:A关联B,同时B也关联A:就是A中有B,同时B中也有A。这样就会造成,A中的Clone克隆的时候,要将 B进行克隆,同时在 B的Clone里面进行 克隆的时候A也就被同时复制了,所以会一直循环复制下去)
8、效果
在运行时增加或删除产品:只要通过客户原型实例即可将新产品类型增加到系统中,例如组态软件中工具箱中的每个工具可以对应一个注册的原型对象,可以通过增加原形对象扩展工具箱。
很容易地创建复杂的对象:在图形编辑和组态等软件中,经常需要创建复杂的图元。这些图元是由简单图元组成的,采用原型模式可以很容易地将复杂图元作为一般图元来使用,使软件的工具箱具有自扩展功能
9、适用性:
1、当一个系统应该独立于产品的创建、构成和表示时,可以使用原型模式。
2、在使用时,我们可以用一些原型对象来代替生成相应对象的工厂对象,并且可以使拷贝、粘贴等操作独立于需要复制的对象。
10、总结
1、Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系(使用者不用关心,类内部的具体情况,直接复制就可以了),它同样要求这些“易变类”拥有“稳定的接口”。
2、Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新的对象(即原型)(只要已经创建一个就可以了),然后在任何需要的地方不断地Clone。
3、Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法(浅拷贝)或者序列化来实现深拷贝。