一、為何需要原型模式(Prototype Pattern)?
在軟件設計中,我們會遇到到這樣的情況:對原對象進行拷貝一個新的副本。想要實現這樣的邏輯,有一種笨方法就是對原對象里的所有變量進行逐一賦值。但是這樣的做法會導致代碼臃腫,邏輯復雜,還可能會有遺漏某些變量沒有被賦值的問題。
為了解決這個在代碼中對每一個變量進行賦值的問題,同時又保證能夠實現拷貝的功能,于是原型模式就出來了。
定義: 用一個已經創建的實例作為原型,來創建一個與原型對象相同的新對象
【角色】
- 抽象原型類: 規定具體原型對象必須實現的clone()方法
- 實現原型類: 實現抽象原型類的clone()方法。
原型模式分為淺克隆和深克隆
淺克隆(淺拷貝):創建一個新的對象,新對象的屬性和原型對象的完全相同。對對象里的引用類型成員,仍指向原來所指向的內存地址。(這里屬性指的是:值類型。同下)
深克隆(深拷貝):創建一個新對象,對對象里的屬性和引用類型成員都會被克隆,引用類型成員不再指向原來的地址。
應用條件:
- 對象里有非常多成員,尤其該對象的引用類型成員里還有其他的引用類型對象。
二、例子
需求:
假設有一盞燈A,然后另一盞燈B根據A燈的樣子造出來的。可以說這兩盞燈外觀一摸一樣。在黑暗中,要想實現一個能有照明系統的用途,這兩盞燈還需要連接一塊電池才能發光照明。
假定電池為引用類型成員,A燈和B燈屬于值類型成員。
A燈+電池 = 原對象。
B燈 +電池 = 新對象。
現有兩種情況可以選擇:
1)情況(淺拷貝):為了節約電池的成本,把這兩盞燈都連接在同一塊電池。
2)情況(深拷貝):為了這兩盞燈要完全分開互不影響,一盞燈連接一塊電池,所以需要兩塊容量一致的電池。
Serializable: 序列化,只對深拷貝有作用。系統遍歷原對象里所有引用類型成員進行逐一克隆,無需代碼來處理具體的過程。被標記為 Serializable 的類才能夠使用深拷貝機制,否則程序運行會崩潰。
//引用類型:電池類[Serializable]public class Battery{public int Capacity;}//燈的原型抽象類[Serializable]public abstract class LampPrototype{public abstract int Use();public abstract LampPrototype ShallowClone();//淺拷貝public abstract LampPrototype DeepClone();//深拷貝}//燈的實現類[Serializable]public class Lamp : LampPrototype{//引用類型,電池public Battery battery = new Battery();public override int Use(){return battery.Capacity;}//淺拷貝public override LampPrototype ShallowClone(){return (Lamp)this.MemberwiseClone();}//深拷貝public override LampPrototype DeepClone(){MemoryStream stream = new MemoryStream();BinaryFormatter formatter = new BinaryFormatter();formatter.Serialize(stream, this);stream.Position = 0;return (Lamp)formatter.Deserialize(stream);}}class Program{//設置原對象的數據public static void setOriginal(LampPrototype lampPro) {var lamp = lampPro as Lamp;lamp.battery.Capacity = 100;}//設置新對象的數據public static void setNew(LampPrototype lampPro) {var lamp = lampPro as Lamp;lamp.battery.Capacity = 80;Console.WriteLine("New Capacity::80");}static void Main(string[] args){//原對象LampPrototype Originallamp = new Lamp();setOriginal(Originallamp);//淺拷貝Console.WriteLine();Console.WriteLine("After ShallowCopy,Original:");LampPrototype shallowAudio = Originallamp.ShallowClone();setNew(shallowAudio);//新對象使用電池,原對象的電池容量受到影響Console.WriteLine("Original Capacity:" + Originallamp.Use());//恢復原對象數據setOriginal(Originallamp);//深拷貝Console.WriteLine();Console.WriteLine("After DeepCopy,Original:");LampPrototype deepAudio = Originallamp.DeepClone();setNew(deepAudio);//新對象使用電池,原對象的電池容量沒有受到影響Console.WriteLine("Original Capacity:" + Originallamp.Use());Console.ReadLine();}}