1. 程式人生 > >設計模式-建立型-原型模式

設計模式-建立型-原型模式

引言:

  原型模式是什麼?它是在什麼場景下被提出的呢?本章節,我們將詳細瞭解下原型模式。

  在軟體系統中,當建立一個類的例項過程過於昂貴或複雜,並且我們需要建立多個這樣類的例項時,如果我們通過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&n