問題:
在程式設計中,我們可能由於某種特定的需要,複製一個對象的結構,與對象的值,動態擷取擷取對象運行時的狀態,最簡單的解決辦法是 new 一個對象新的對象,然後將對象的屬性值一一賦值給新的對象,這樣要一一列出對象的屬性賦值,操作起來太複雜,而且及其容易出錯,落下某個屬性的賦值。能否讓對象本身提供一個自我複製的功能,用戶端只需簡單的調用這個方法就能完成對象的複製。
定義:
用原型執行個體指定建立對象的種類,並且通過拷貝這些原型建立新的對象。
意圖:
Prototype模式允許一個已存在的對象通過建立的介面(Clone),通過拷貝自己來實施建立另外一個可定製的對象(定製好屬性值然後批量複製),根本無需知道任何如何建立的細節。Clone)。
Clone()實現和具體的實現語言相關。.NET中一般使用Object的MemberwiseClone()方法建立對象的淺表副本實現淺複製。
參與者:
•Prototype(抽象原型)角色:
抽象原型角色:這是一個抽象角色,通常由一個介面或抽象類別實現。此角色給出所有的具體原型類所需的介面(.NET提供了一個定義方法Clone()方法的ICloneable介面,作為系統中的通用Prototype)。
•ConcretePrototype(抽象原型)角色:
被複製的對象。此角色需要實現抽象的原型角色所要求的介面。實現具體的對象複製方法。
UML:
淺複製與深複製:
•淺複製:如果欄位是實值型別的(包括string),則對該欄位執行逐位複製,如欄位是參考型別,則複製引用但不自製引用的對象。因此,原始對象及其複本引用同一對象。通過Object的MemberwiseClone()方法即可實現。
•深複製:如果欄位是實值型別的,則對該欄位執行逐位複製,如欄位是參考型別,則複製引用對象的變數指向複製過的新對象,而不是原有的被引用的對象。讓子類也繼承Prototype角色幾口實現Clone()方法,使用逐層複製賦值參考型別欄位的辦法完成。
執行個體說明:
諾基亞手機工廠
同款型號的手機生產多個不用每次都要通過生產工廠,可以根據已生產好的某款通過複製的辦法來完成。
uml圖如下:
代碼:
淺複製:
/// <summary>
/// 抽象原型角色
/// </summary>
public interface IPrototypePhone
{
object Clone();
}
/// <summary>
/// 具體原型角色
/// </summary>
public class ConcretePrototypePhone : IPrototypePhone
{
public string Name { get; set; }
public string Cpu { get; set; }
public string Mb { get; set; }
public float MemorySize { get; set; }
public string System { get; set; }
public object Clone()
{
//使用內建MemberwiseClone()方法複製對象
return this.MemberwiseClone();
}
}
public void PrototypeTest()
{
ConcretePrototypePhone obj = new ConcretePrototypePhone();
obj.Cpu = "ARM_27";
obj.Mb = "N8_1op";
obj.MemorySize = 375;
obj.Name = "N8";
obj.System = "塞班";
var n8 = obj.Clone();
}
深複製:
/// <summary>
/// 具體原型角色,實現.net提供的通用 抽象原型角色System.ICloneable
/// </summary>
public class ConcretePrototypePhone : System.ICloneable
{
public string Name { get; set; }
public ConcretePrototypeCpu Cpu { get; set; }
public string Mb { get; set; }
public float MemorySize { get; set; }
public string System { get; set; }
public object Clone()
{
//看複製父物件
ConcretePrototypePhone obj = (ConcretePrototypePhone)this.MemberwiseClone();
//複製子物件,將子物件的引用指向複製出的新對象
obj.Cpu = (ConcretePrototypeCpu)this.Cpu.Clone();
return obj;
}
}
public class ConcretePrototypeCpu : System.ICloneable
{
public string Name { get; set; }
public float Frequency { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
public void PrototypeTest()
{
ConcretePrototypePhone obj = new ConcretePrototypePhone();
obj.Cpu = new ConcretePrototypeCpu() { Frequency = 2.4F, Name = "Ar700" };
obj.Mb = "N8_1op";
obj.MemorySize = 375;
obj.Name = "N8";
obj.System = "塞班";
ConcretePrototypePhone n8 = (ConcretePrototypePhone)obj.Clone();
n8.Cpu = new ConcretePrototypeCpu() { Frequency = 2.0F, Name = "Ar70" };
}
優點:
•可在不用重新初始化對象,而是動態地擷取對象運行時的狀態。
•屏蔽對象的複製細節,用戶端無需關心複製細節,對象的複製被封閉在對象內部,怎麼樣複製有對象本身決定。
•在某些環境下,複製對象比建立新對象更有效,因為對象的建構函式是不會執行的。
缺點:
•實現深層複製時實現比較複雜。
應用情景:
•複製對象的結構與資料。
•希望對目標對象的修改不影響既有的原型對象。
•建立對象成本較大的情況下(初始化需佔用較長時間,佔用太多CPU資源或網路資)
PS:
•在調用MemberwiseClone進行淺複製時,原型模式是記憶體二進位流的拷貝,建構函式是不會執行的。