原型模式從字面上來看, 可能還不是那麼通俗, 通俗點講, 可以說是拷貝模式.
從拷貝來說, 有完全拷貝, 和不完全拷貝. 就仿佛孫猴子的吹毛生猴, 但是這些小猴子明顯就沒有孫悟空本體厲害, 這種拷貝, 算是淺拷貝吧.
既然有淺拷貝, 那肯定也是有深拷貝的. 深拷貝就是小猴子與孫悟空本體一樣厲害, 無論是從本事還是從長相, 都是一樣的
一、原型
/// <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方法, 可以用來克隆表結構和約束, 相當方便, 我不需要再去做一遍繁雜的表結構創建.
參考:
大話設計模式
C#設計模式(9)