原型模式概述 定義:使用原型實例指定待創建對象的類型,並且通過複製這個原型來創建新的對象。簡單的來說就是克隆(Clone),通過已經存在的,將其複製而產生新的。原型模式屬於創建型模式,將一個原型對象傳給要發動創建的對象(客戶端對象),該對象通過請求原型對象複製自己來實現創建過程。 既然是通過Clon ...
- 原型模式概述
定義:使用原型實例指定待創建對象的類型,並且通過複製這個原型來創建新的對象。簡單的來說就是克隆(Clone),通過已經存在的,將其複製而產生新的。原型模式屬於創建型模式,將一個原型對象傳給要發動創建的對象(客戶端對象),該對象通過請求原型對象複製自己來實現創建過程。
既然是通過Clone創建的,那麼就會存該拷貝是淺拷貝還是深拷貝的問題了。
淺拷貝(Shallow Clone):當原型對象被覆制時,只複製它本身和其中包含的值類型的成員變數,而引用類型的成員變數並沒沒複製。如我想將A複製一份出來,命名為為B,那麼我在淺拷貝後,確實可以得到A和B。而且A和B的值也相等,但是,我將B的值稍作修改,A的值也會變動,這往往不是我們想要的。因為我們想拷貝一個副本出來,二者也能獨立,這樣才算拷貝。但是,淺拷貝後A和B卻是指向同一片地址空間,也就是二者共用一個值,改一個,兩個都變。
深拷貝(Deep Clone):除了對象本身被覆制外,對象所包含的所有成員變數也被覆制,就是我們想要的那種拷貝,即有一個副本,與原者老死不想往來,互不影響
原型模式的實現 :
結構:
- Prototype(抽象原型類):聲明克隆方法的介面,所有具體原型類的公共父類,抽象類、介面皆可,也可是具體類
- ConcretePrototpye(具體原型類):實現在抽象原型類中聲明的克隆方法,返回自己的克隆對象
- Client(客戶類):讓一個原型對象克隆自身而創建一個新的對象,直接實例化或通過工廠方法創建一個原型對象,在調用克隆方法即可
- 原型模式應用實例
問題描述:為某銷售管理系統設計並實現一個客戶類Customer,在客戶類中包含一個名為客戶地址的成員變數,客戶地址的類型為Address,用淺克隆和深克隆分別實現Customer對象的複製,並比較這兩種克隆方式的異同(異同前面簡單說了,就不重覆了)
結構:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.Serialization.Formatters.Binary; //序列化 6 using System.IO;//文件 7 using System.Runtime.Serialization;//序列化異常處理 8 9 namespace Customer 10 { 11 [Serializable]//將Customer類設置為可序列化 12 public abstract class Customer : ICloneable 13 { 14 public abstract object Clone(); 15 } 16 17 [Serializable]//將Address類設置為可序列化 18 public class Address//地址類 19 { 20 private string province; 21 private string city; 22 23 public string City 24 { 25 get { return city; } 26 set { city = value; } 27 } 28 29 public string Province 30 { 31 get { return province; } 32 set { province = value; } 33 } 34 35 public Address(string province,string city) 36 { 37 this.province = province; 38 this.city = city; 39 } 40 41 public override string ToString()//列印地區 42 { 43 return "地址為:" + province + " 省," + city + " 市。"; 44 } 45 } 46 47 [Serializable]//將CustomerA設置為可序列化 48 public class CustomerA : Customer//顧客A類 49 { 50 private string name; 51 private int age; 52 private string call; 53 private Address address; 54 55 public Address Address 56 { 57 get { return address; } 58 set { address = value; } 59 } 60 61 public string Name 62 { 63 get { return name; } 64 set { name = value; } 65 } 66 67 public int Age 68 { 69 get { return age; } 70 set { age = value; } 71 } 72 73 public string Call 74 { 75 get { return call; } 76 set { call = value; } 77 } 78 79 public CustomerA(string name, int age, string call,Address address) 80 { 81 this.name = name; 82 this.age = age; 83 this.call = call; 84 this.address = address; 85 } 86 87 public override string ToString() 88 { 89 return "客戶A--姓名:" + this.name + " 年齡:" + this.age + " 聯繫方式:" + this.call + " "+ this.address.ToString(); 90 } 91 92 #region 淺克隆+this.MemberwiseClone() 93 public object MemClone() 94 { 95 return this.MemberwiseClone(); 96 } 97 #endregion 98 99 #region 深克隆+object Clone() 100 public override object Clone() 101 { 102 Kits.FileSer(@"d:\1.txt", this);// 103 object obj = Kits.FileDSer(@"d:\1.txt"); 104 return obj; 105 } 106 #endregion 107 } 108 109 [Serializable]//將CustomerB設置為可序列化 110 public class CustomerB : Customer//顧客B類 111 { 112 private string name; 113 private int age; 114 private string call; 115 private Address address; 116 117 public Address Address 118 { 119 get { return address; } 120 set { address = value; } 121 } 122 123 public string Name 124 { 125 get { return name; } 126 set { name = value; } 127 } 128 129 public int Age 130 { 131 get { return age; } 132 set { age = value; } 133 } 134 135 public string Call 136 { 137 get { return call; } 138 set { call = value; } 139 } 140 141 public CustomerB(string name, int age, string call, Address address) 142 { 143 this.name = name; 144 this.age = age; 145 this.call = call; 146 this.address = address; 147 } 148 149 public override string ToString() 150 { 151 return "客戶B--姓名:" + this.name + " 年齡:" + this.age + " 聯繫方式:" + this.call + " " + this.address.ToString(); 152 } 153 154 #region 淺克隆+this.MemberwiseClone() 155 public object MemClone() 156 { 157 return this.MemberwiseClone(); 158 } 159 #endregion 160 161 #region 深克隆+object Clone() 162 public override object Clone() 163 { 164 Kits.FileSer(@"d:\1.txt", this);//讀 165 object obj = Kits.FileDSer(@"d:\1.txt");//寫 166 return obj; 167 } 168 #endregion 169 } 170 171 public class Kits//工具類 172 { 173 public static void FileSer(string path,object obj)//讀出信息 174 { 175 FileStream fs = new FileStream(path, FileMode.OpenOrCreate);//通道 序列化 176 BinaryFormatter bf = new BinaryFormatter();//搬運工 177 try 178 { 179 bf.Serialize(fs, obj);//序列化 180 } 181 catch (SerializationException e) 182 { 183 Console.WriteLine("該文件進行序列化失敗。原因 : " + e.Message); 184 throw; 185 186 } 187 finally { fs.Close(); } 188 } 189 190 public static object FileDSer(string path)//寫入 191 { 192 FileStream fs = new FileStream(path, FileMode.Open);//通道、路徑,許可權 193 BinaryFormatter bf = new BinaryFormatter();//搬運工 194 object obj = null; 195 try 196 { 197 obj=bf.Deserialize(fs);//反序列化 198 } 199 catch (SerializationException e) 200 { 201 Console.WriteLine("該文件進行反序列化失敗。原因 : " + e.Message); 202 throw; 203 204 } 205 finally 206 { 207 fs.Close(); 208 } 209 return obj; 210 } 211 } 212 213 class Program 214 { 215 static void Main(string[] args) 216 { 217 Console.WriteLine("\n--------------------------------Customer-------------------------------------"); 218 Address addr1 = new Address("中國江蘇", "揚州"); 219 CustomerA c1 = new CustomerA("社會人c1", 20, "13288888888", addr1); 220 Console.WriteLine(c1.ToString());//c1的作為原對象 221 222 Console.WriteLine("\n-----------------------------------Copy(c2 = c1)-------------------------------------"); 223 CustomerA c2 = c1; 224 Console.WriteLine("社會人c2,更新信息.直接複製對象\n"); 225 Console.WriteLine(); 226 Console.WriteLine(c2.ToString()); 227 228 Console.WriteLine("\n-----------------------------ShallowCopy-------------------------------------"); 229 Console.WriteLine(); 230 Console.WriteLine("社會人c3,更新信息\n"); 231 CustomerA c3 = (CustomerA)c1.MemClone();//淺克隆 232 Console.WriteLine(c3.ToString()); 233 234 Console.WriteLine("此時 c2:"); 235 Console.WriteLine(c2.ToString()); 236 237 Console.WriteLine("\n--------------------------------Customer-------------------------------------\n"); 238 Console.WriteLine(); 239 Address addr2 = new Address("中國廣東", "廣州"); 240 CustomerB c4 = new CustomerB("小豬佩琪", 24, "16612345678", addr2); 241 Console.WriteLine("c4 "+c4.ToString()); 242 Console.WriteLine("\n--------------------------------DeepCopy(update c5.Age = 26)--------------------------\n"); 243 244 CustomerB c5 = (CustomerB)c4.Clone(); 245 c5.Age = 26; 246 Console.WriteLine("一年後,搬家\n"); 247 c5.Address = new Address("中國天津", "河西區"); 248 Console.WriteLine(c5.ToString()); 249 250 Console.WriteLine("此時 c4:"); 251 Console.WriteLine(c4.ToString()); 252 ; 253 } 254 } 255 }View Code
運行結果:
上述FileStream類和BinaryFormatter類是實現對象序列化和反序列化的操作的,就是用序列化把當前對象寫入流中,流入文件,再用反序列化從文件中流出對象。文件相當於中介,當然,中介還可以是記憶體、網路。
- 原型管理器
簡單介紹:原型管理器(Prototype Manager)就是將多個原型對象存儲在一個集合中供客戶端使用,它是一個專門負責克隆對象的工廠,其中定義了一個集合用於存儲原型對象,如果需要某個原型對象的一個克隆,可以通過複製集合中對應的原型對象獲得。
1 using System.Collections; 2 class PrototypeManager 3 { 4 Hashtable ht = new Hashtable(); //使用Hashtable存儲原型對象 5 public PrototypeManager() 6 { 7 ht.Add("A", new ConcretePrototypeA()); 8 ht.Add("B", new ConcretePrototypeB()); 9 } 10 public void Add(string key, Prototype prototype) 11 { 12 ht.Add(key,prototype); 13 } 14 public Prototype Get(string key) 15 { 16 Prototype clone = null; 17 clone = ((Prototype)ht[key]).Clone(); //通過克隆方法創建新對象 18 return clone; 19 } 20 }View Code
原型模式的優缺點和適用環境
- 原型模式的優點:
- 簡化對象的創建過程,通過複製一個已有對象實例可以提高新實例的創建效率
- 擴展性好
- 提供了簡化的創建結構,原型模式中的產品的複製是通過封裝在原型類中的克隆方法實現的,無需專門的工廠類來創建產品
- 可以通過深克隆的方式保存對象的狀態,使用原型模式將對象複製一份並其狀態保存起來,以便在需要的時候使用,可輔助實現撤銷操作
- 原型模式的缺點:
- 需要為每一個類準備一個克隆方法,而且該克隆方法位於一個類的內部,當對已有類進行改造時,需要修改原代碼,違背了開閉原則
- 在實現深克隆時需要寫較複雜的代碼,而且當對象之間存在多重的嵌套引用時,為了實現深克隆,每一層對象對應的類必須支持深克隆,實現起來較煩較煩....
- 原型模式的適用環境:
- 創建新對象成本較大,新對象可以通過複製已有對象來獲得,如果是相似對象,則可以對其成員變數修改
- 系統要保存對象的狀態,而對象的狀態變化很小
- 需要避免使用分層次的工廠類來創建分層次的對象,並且類的實例對象只有一個或很少的幾個組合狀態,通過複製原型對象得到新實例可能比使用構造函數創建一個新實例更方便