设计模式学习笔记-原型模式
一、概述
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象;
二、模式中的角色
Prototype:声明一个克隆自身的接口;
ConcretePrototype:实现一个克隆自身的操作;
三、UML类图
四、代码实现
/// <summary> /// 原型类,声明一个克隆自身的接口 /// </summary> public abstract class Prototype { private string name; public string Name { get { return name; } } public Prototype(string name) { this.name = name; } public abstract Prototype Clone(); } /// <summary> /// 具体原型类,实现克隆自身的操作 /// </summary> public class ConcretePrototype1 : Prototype { public ConcretePrototype1(string name) : base(name) { } public override Prototype Clone() { ///MemberwiseClone 创建当前对象的浅表副本 return (Prototype)this.MemberwiseClone(); } } /// <summary> /// 具体原型类,实现克隆自身的操作 /// </summary> public class ConcretePrototype2 : Prototype { public ConcretePrototype2(string name) : base(name) { } public override Prototype Clone() { ///MemberwiseClone 创建当前对象的浅表副本 return (Prototype)this.MemberwiseClone(); } } #region 原型模式 ConcretePrototype1 p1 = new ConcretePrototype1("原型模式"); Console.WriteLine("name:{0}", p1.Name); ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone(); Console.WriteLine("name:{0}", c1.Name); #endregion
五、实际应用
在.NET里,那个原型抽象类Prototype是用不着的,因为.NET在System命名空间中提供了ICloneable接口,其中唯一一个方法就是Clone(),所以我们只需要实现这个接口就可以完成原型模式了;
在实际当中又分了浅复制和深复制两种情况,因为MemberwiseClone是复制当前对象浅表副本,我们来看看2种模式的区别;
5.1 浅复制:
/// <summary> /// 小狗的颜色 /// </summary> public class DogColor { public string Color { get; set; } } /// <summary> ///小狗类 ,继承ICloneable /// </summary> public class Dog : ICloneable { /// <summary> /// 名字 /// </summary> public string Name{ get; set; } /// <summary> /// 重量 /// </summary> public int Weight { get; set; } /// <summary> /// 小狗颜色对象 /// </summary> public DogColor dogColor { get; set; } public object Clone() { ///创建当前对象的浅表副本 return this.MemberwiseClone(); } public override string ToString() { string str = string.Format("{0}是{1},体重{2}", Name, dogColor.Color, Weight); return str; } } #region 浅复制 Dog dog = new Dog(); dog.Name= "小黑"; dog.Weight = 100; dog.dogColor = new DogColor() { Color = "黑色" }; Console.WriteLine(dog.ToString()); Dog dog1 = (Dog)dog.Clone(); dog1.name = "小白"; dog1.Weight = 200; dog1.dogColor.Color = "白色"; Console.WriteLine(dog1.ToString()); Console.WriteLine(dog.ToString()); #endregion
运行上面的代码得到的结果:
分析上面的代码的运行情况:
我们得出以下的结论对于值类型的成员,浅复制也是在副本中重新创建的成员,对应到内存的栈上,分配新的内存空间。那么对于引用类型则因为浅复制的时候,对象和对象副本共用同一个引用对象,那么不管是在对象还是对象副本中修改了相应的引用成员了之后,
那么这个引用类型的成员就会发生变化。此处最好的例子就是dog1对象进行浅度复制后修改了颜色导致dog对象的颜色发生了变化。因为2个对象指向同一个内存地址,那么任何一个修改操作都会产生改变。使用深度复制就可以解决这一问题。
5.2 深复制
我们修改Dog类里的Clone方法,如下:
public object Clone() { ///创建当前对象的浅表副本 Dog dog = (Dog)this.MemberwiseClone(); DogColor dogColor = new DogColor(); dogColor.Color = this.dogColor.Color; dog.dogColor = dogColor; return dog; }
运行结果:
不管是值类型的成员还是引用类型的成员,这样的对象和对象副本,对任何一个成员属性的修改,都不会影响到改变对象的值。使用这样的方式完成对象的深度复制。
六、应用场景
6.1 当一个系统应该独立于它的产品创建、构成和表示时;
6.2 当要实例化的类是在运行时刻指定时,例如通过动态转载;
6.3 为了避免创建一个与产品类层次平行的工厂类层次时;
6.4 当一个类的实例只能有几个不同状态组合中的一种时;建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些;
参考书籍《设计模式-可复用面向对象软件的基础》