引言: 原型模式是什麼?它是在什麼場景下被提出的呢?本章節,我們將詳細瞭解下原型模式。 在軟體系統中,當創建一個類的實例過程過於昂貴或複雜,並且我們需要創建多個這樣類的實例時,如果我們通過new來創建類實例,這就會增加創建類的複雜度和創建過程與客戶代碼複雜的耦合度。如果採用工廠模式來創建這樣的實例對 ...
引言:
原型模式是什麼?它是在什麼場景下被提出的呢?本章節,我們將詳細瞭解下原型模式。
在軟體系統中,當創建一個類的實例過程過於昂貴或複雜,並且我們需要創建多個這樣類的實例時,如果我們通過new來創建類實例,這就會增加創建類的複雜度和創建過程與客戶代碼複雜的耦合度。如果採用工廠模式來創建這樣的實例對象的話,隨著產品類的不斷增加,導致子類的數量不斷增多,也導致了相應工廠類的增加,維護的代碼維度增加了,因為有產品和工廠兩個維度了,反而增加了系統複雜程度,所以在這裡使用工廠模式來封裝類創建過程並不合適。由於每個類實例都是相同的(類型相同),但是每個實例的狀態參數會有不同,如果狀態數值也相同就沒意義了,有一個這樣的對象就可以了。當我們需要多個相同的類實例時,可以通過對原來對象拷貝一份來完成創建,這個思路正是原型模式的實現方式。
定義:
原型模式就是通過給出一個原型對象來指明所要創建的對象類型,然後用複製這個對象的方法來創建更多的同類型對象。
原型模式的兩種類型:
object類的clone方法只會拷貝對象中基本的數據類型,對於數組、容器對象、引用對象等都不會拷貝,這就是淺拷貝。如果要實現深拷貝,必須將原型模式中的數組、容器對象、引用對象等另行拷貝。
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 SunWukong sunwukong = new SunWukong() 6 { 7 Id = 1, 8 Name = "孫悟空", 9 Weapon = new Weapon() 10 { 11 Name = "如意金箍棒" 12 } 13 }; 14 SunWukong xingzhesun = (SunWukong)sunwukong.Clone(); 15 Console.WriteLine($"{sunwukong.Id}-{sunwukong.Name}-{sunwukong.Weapon.Name}"); 16 Console.WriteLine($"{xingzhesun.Id}-{xingzhesun.Name}-{xingzhesun.Weapon.Name}"); 17 Console.WriteLine("==============================="); 18 19 // 驗證克隆後屬性之間是否共用 20 xingzhesun.Id = 2; 21 xingzhesun.Name = "行者孫"; 22 xingzhesun.Weapon.Name = "釘耙"; 23 Console.WriteLine($"{xingzhesun.Id}-{xingzhesun.Name}-{xingzhesun.Weapon.Name}"); 24 Console.WriteLine($"{sunwukong.Id}-{sunwukong.Name}-{sunwukong.Weapon.Name}"); 25 // 從結果可以看出,對於基本類型屬性,克隆之後不共用,而對於對象來說是共用的,這就是淺拷貝 26 Console.WriteLine("==============================="); 27 Console.WriteLine($"孫悟空的武器{sunwukong.Weapon.GetHashCode()},行者孫的武器{sunwukong.Weapon.GetHashCode()}"); 28 // 列印hashcode值可以看出,克隆後的實例與原型的Weapon指向同一個地址 29 } 30 } 31 32 internal class SunWukong : ICloneable 33 { 34 public int Id { get; set; } 35 public string Name { get; set; } 36 public Weapon Weapon { get; set; } 37 38 public object Clone() 39 { 40 return this.MemberwiseClone(); 41 } 42 } 43 44 internal class Weapon 45 { 46 public string Name { get; set; } 47 }view code
.net中提供了原型,即System命名空間下的介面ICloneable,只要實現該介面的Clone方法就可以之間原型拷貝。上述代碼中遺留了一個問題,如何實現深拷貝問題。
1 internal class Program 2 { 3 private static void Main(string[] args) 4 { 5 SunWukong sunwukong = new SunWukong() 6 { 7 Id = 1, 8 Name = "孫悟空", 9 Weapon = new Weapon() 10 { 11 Name = "如意金箍棒" 12 } 13 }; 14 SunWukong xingzhesun = (SunWukong)sunwukong.Clone(); 15 // 引用類型再進行拷貝 16 xingzhesun.Weapon = (Weapon)xingzhesun.Weapon.Clone(); 17 Console.WriteLine($"{sunwukong.Id}-{sunwukong.Name}-{sunwukong.Weapon.Name}"); 18 Console.WriteLine($"{xingzhesun.Id}-{xingzhesun.Name}-{xingzhesun.Weapon.Name}"); 19 Console.WriteLine("==============================="); 20 xingzhesun.Id = 2; 21 xingzhesun.Name = "行者孫"; 22 xingzhesun.Weapon.Name = "釘耙"; 23 Console.WriteLine($"{xingzhesun.Id}-{xingzhesun.Name}-{xingzhesun.Weapon.Name}"); 24 Console.WriteLine($"{sunwukong.Id}-{sunwukong.Name}-{sunwukong.Weapon.Name}"); 25 Console.WriteLine("==============================="); 26 Console.WriteLine($"孫悟空的武器{sunwukong.Weapon.GetHashCode()},行者孫的武器{xingzhesun.Weapon.GetHashCode()}"); 27 } 28 } 29 30 internal class SunWukong : ICloneable 31 { 32 public int Id { get; set; } 33 public string Name { get; set; } 34 public Weapon Weapon { get; set; } 35 36 public object Clone() 37 { 38 return this.MemberwiseClone(); 39 } 40 } 41 42 internal class Weapon : ICloneable 43 { 44 public string Name { get; set; } 45 46 public object Clone() 47 { 48 return this.MemberwiseClone(); 49 } 50 }view code
深拷貝:
1、複製對象的基本數據類型的成員變數值。
2、為所有引用類型的成員變數申請存儲空間,並複製每個引用數據類型成員變數所引用的對象,直到該對象可達的所有對象。也就是說,對象進行深拷貝要對整個對象進行拷貝。
3、深拷貝實現方式1:重寫clone方法。
4、深拷貝實現方式2:通過對象序列化。
1 //對象深拷貝 2 public static T Copy<T>(T oldObject) where T : class,new() 3 { 4 T newOrder = new T(); 5 MemoryStream stream = new MemoryStream(); 6 BinaryFormatter bf = new BinaryFormatter(); 7 bf.Serialize(stream, oldObject); 8 stream.Position = 0; 9 newOrder = (bf.Deserialize(stream) as T); 10 return newOrder; 11 }view code
原型模式的註意事項和細節:
1、創建新的對象比較複雜時,可以利用原型模式簡化對象的創建過程,同時也能夠提高效率。
2、不用重新初始化對象,而是動態地獲取對象運行時的狀態。
3、如果原始對象發生變化(增加或減少屬性),其它克隆對象也會發生相應的變化,無需修改代碼。
4、在實現深克隆的時候可能需要比較複雜的代碼。
5、使用原型模式複製不會調用類的構造方法。因為對象的複製是通過調用clone方法完成的,它直接在記憶體種複製數據,因此不會調用到類的構造方法。不但構造方法中的代碼不會執行,甚至連訪問許可權都對原型模式無效。單例模式中,我們通過私有化構造函數來實現單例模式,但clone方法直接無視構造方法的許可權,所以,單例模式與原型模式是衝突的。
原型模式的優點:
1、原型模式向客戶隱藏了創建新實例的複雜性。
2、原型模式允許動態增加或較少產品類。
3、原型模式簡化了實例的創建結構,工廠方法模式需要有一個與產品類等級結構相同的等級結構,而原型模式不需要這樣。
4、產品類不需要事先確定產品的等級結構,因為原型模式適用於任何的等級結構。
缺點:
1、需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改源碼,即違反了OCP原則。
參考:https://www.cnblogs.com/zhili/p/PrototypePattern.html
https://yq.aliyun.com/articles/485574