原型模式从字面上来看, 可能还不是那么通俗, 通俗点讲, 可以说是拷贝模式.
从拷贝来说, 有完全拷贝, 和不完全拷贝. 就仿佛孙猴子的吹毛生猴, 但是这些小猴子明显就没有孙悟空本体厉害, 这种拷贝, 算是浅拷贝吧.
既然有浅拷贝, 那肯定也是有深拷贝的. 深拷贝就是小猴子与孙悟空本体一样厉害, 无论是从本事还是从长相, 都是一样的
一、原型
/// <summary> /// 原型 /// </summary> abstract public class Prototype { public int Id { get; set; } public string Name { get; set; } public List<int> Size = new List<int>(); public Prototype(int id) { this.Id = id; } abstract public Prototype Clone(); }
原型上, 我放了一个值类型, 一个字符串, 一个整形集合, 在原型模式下, 我会修改这几个值, 来观察克隆之后的变化
二、浅拷贝
public class Concrete1 : Prototype { public Concrete1(int id) : base(id) { } public override Prototype Clone() { return (Prototype)this.MemberwiseClone(); } }
测试代码:
Console.WriteLine("--------------浅拷贝----------------------"); Concrete1 p1 = new Concrete1(1); p1.Name = "Name"; p1.Size.AddRange(new int[] { 1, 2, 3, 4, 5 }); Concrete1 c1 = (Concrete1)p1.Clone(); Console.WriteLine("Before Concrete1 change"); Console.WriteLine("Cloned : Id - {0}, Name - {1}, Size - {2}", c1.Id, c1.Name, string.Join(",", c1.Size)); Console.WriteLine(); p1.Id = 2; p1.Name = "p1.Name"; p1.Size.AddRange(new int[] { 6, 7, 8, 9 }); Console.WriteLine("After Concrete1 changed"); Console.WriteLine("Cloned : Id - {0}, Name - {1}, Size - {2}", c1.Id, c1.Name, string.Join(",", c1.Size));
结果:
可以看到, 我在克隆之后, 修改了原型集合的值, 克隆对象也跟着改变了, 说明他们的集合变量指向同一块堆空间.
Console.WriteLine("p1.Size == c1.Size, {0}", p1.Size == c1.Size);
有图有真相.
三、深拷贝
public class Concrete2 : Prototype { public Concrete2(int id) : base(id) { } public override Prototype Clone() {var jsonStr = JsonConvert.SerializeObject(this);return JsonConvert.DeserializeObject<Concrete2>(jsonStr); } }
测试代码:
Console.WriteLine("--------------深拷贝----------------------"); Concrete2 p2 = new Concrete2(2); p2.Name = "Name"; p2.Size.AddRange(new int[] { 1, 2, 3, 4, 5 }); Concrete2 c2 = (Concrete2)p2.Clone(); Console.WriteLine("Before Concrete2 change"); Console.WriteLine("Cloned : Id - {0}, Name - {2}, Size - {2}", c2.Id, c2.Name, string.Join(",", c2.Size)); Console.WriteLine(); p2.Id = 2; p2.Name = "p2.Name"; p2.Size.AddRange(new int[] { 6, 7, 8, 9 }); Console.WriteLine("After Concrete2 changed"); Console.WriteLine("Cloned : Id - {0}, Name - {2}, Size - {2}", c2.Id, c2.Name, string.Join(",", c2.Size)); Console.WriteLine("p2.Size == c2.Size, {0}", p2.Size == c2.Size);
结果:
可以看到, 拷贝实体并没有任何变化, 说明他们已经是两个独立的分开的实体. 并没有共同的变量引用(方法引用除外).
在这里, 我实现深拷贝的方式是通过序列化的方式, 当然还有很多别的方式. 可以自己实现. C#提供的那个克隆方法, 是浅拷贝的实现.
C#有一个接口:ICloneable, 如果不想做成抽象类, 也可以通过这个接口去实现.
四、个人应用
就我个人在项目中的应用来看, 最近使用过结构克隆.
Datatble这个变量, 无论是b/s, 还是c/s, 应该都是用过的吧.
它有一个Clone方法, 可以用来克隆表结构和约束, 相当方便, 我不需要再去做一遍繁杂的表结构创建.
参考:
大话设计模式