一、定義
原型模式:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
解釋:有一個設計非常復雜的對象,如果需要得到多個這樣對象的時候,可以先創建一個原型對象,然后使用原型對象clone出新的對象,從而實現減少內存消耗和類實例復用的目的。
二、UML類圖及基本代碼

基本代碼:
abstract class PRototype { private string id; public string ID { get { return id; } } public Prototype(string id) { this.id = id; } public abstract Prototype Clone(); } class ConcretePrototype : Prototype { public ConcretePrototype(string id) : base(id) { } public override Prototype Clone() { return (Prototype)this.MemberwiseClone(); } }
客戶端調用:
ConcretePrototype cp1 = new ConcretePrototype("a"); ConcretePrototype cp2 = (ConcretePrototype)cp1.Clone();
三、具體實例
編寫一個簡歷,包含姓名、性別、年齡、工作經歷等,然后復制多份進行顯示。
實例代碼及運行結果:

class Program { static void Main(string[] args) { Resume resume1 = new Resume("tom"); resume1.SetPersonInfo("man", "17"); resume1.SetWorkExperience("1980-1990", "xx company"); Resume resume2 = (Resume)resume1.Clone(); resume2.SetWorkExperience("1990-2000", "yy company"); Resume resume3 = (Resume)resume1.Clone(); resume3.SetPersonInfo("man", "19"); resume1.Display(); resume2.Display(); resume3.Display(); Console.Read(); } } class Resume : ICloneable { private string name; private string sex; private string age; private string timeArea; private string company; public Resume(string name) { this.name = name; } public void SetPersonInfo(string sex, string age) { this.sex = sex; this.age = age; } public void SetWorkExperience(string timeArea, string company) { this.timeArea = timeArea; this.company = company; } public void Display() { Console.WriteLine("{0} {1} {2}", name, sex, age); Console.WriteLine("workexperience:{0} {1}", timeArea, company); } public object Clone() { return (object)this.MemberwiseClone(); } }

實例延伸:
上述實例總MemberwiseClone()方法是:如果字段是值類型,則對該字段執行逐位復制,如果字段是引用類型,則復制引用,但不復制引用的對象。這稱之為淺拷貝,被復制對象的所有變量都含有與原來對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。接下來,將工作經歷單獨拿出來作為一個類在簡歷類中引用。如下:

class Program { static void Main(string[] args) { Resume resume1 = new Resume("tom"); resume1.SetPersonInfo("man", "17"); resume1.SetWorkExperience("1980-1990", "xx company"); Resume resume2 = (Resume)resume1.Clone(); resume2.SetWorkExperience("1990-2000", "yy company"); Resume resume3 = (Resume)resume1.Clone(); resume1.SetWorkExperience("2000-2010", "zz company"); resume1.Display(); resume2.Display(); resume3.Display(); Console.Read(); } } class WorkExperience { private string timeArea; public string TimeArea { get { return timeArea; } set { timeArea = value; } } private string company; public string Company { get { return company; } set { company = value; } } } class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; public Resume(string name) { this.name = name; work = new WorkExperience(); } public void SetPersonInfo(string sex, string age) { this.sex = sex; this.age = age; } public void SetWorkExperience(string timeArea, string company) { work.TimeArea = timeArea; work.Company = company; } public void Display() { Console.WriteLine("{0} {1} {2}", name, sex, age); Console.WriteLine("workexperience:{0} {1}", work.TimeArea, work.Company); } public object Clone() { return (object)this.MemberwiseClone(); } }
結果如下:

分析:工作經歷是有3段,但運行結果只顯示一段。由此可見淺拷貝對于類中引用對象的復制并不成功。
四、概念講解
淺拷貝:當對象的字段值被拷貝時,字段引用的對象不會被拷貝。例如,如果一個對象有一個指向字符串的字段,并且我們對該對象做了一個淺拷貝,那么這兩個對象將引用同一個字符串。
深拷貝:對對象實例中字段引用的對象也進行拷貝,如果一個對象有一個指向字符串的字段,并且我們對該對象進行了深拷貝的話,那么我們將創建一個對象和一個新的字符串,新的對象將引用新的字符串。
也就是說,執行深拷貝創建的新對象和原來對象不會共享任何東西,改變一個對象對另外一個對象沒有任何影響,而執行淺拷貝創建的新對象與原來對象共享成員,改變一個對象,另外一個對象的成員也會改變。
五、實例改進
同樣是上述的實例,對于有引用字段的,本例使用深拷貝,代碼如下:

class Program { static void Main(string[] args) { Resume resume1 = new Resume("tom"); resume1.SetPersonInfo("man", "17"); resume1.SetWorkExperience("1980-1990", "xx company"); Resume resume2 = (Resume)resume1.Clone(); resume2.SetWorkExperience("1990-2000", "yy company"); Resume resume3 = (Resume)resume1.Clone(); resume1.SetWorkExperience("2000-2010", "zz company"); resume1.Display(); resume2.Display(); resume3.Display(); Console.Read(); } } class WorkExperience:ICloneable //實現ICloneable接口 { private string timeArea; public string TimeArea { get { return timeArea; } set { timeArea = value; } } private string company; public string Company { get { return company; } set { company = value; } } public object Clone() { return (object)this.MemberwiseClone(); } } class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; public Resume(string name) { this.name = name; work = new WorkExperience(); } private Resume(WorkExperience work) { this.work = (WorkExperience)work.Clone();//提供Clone方法調用的私有構造函數,以便克隆工作經歷的數據 } public void SetPersonInfo(string sex, string age) { this.sex = sex; this.age = age; } public void SetWorkExperience(string timeArea, string company) { work.TimeArea = timeArea; work.Company = company; } public void Display() { Console.WriteLine("{0} {1} {2}", name, sex, age); Console.WriteLine("workexperience:{0} {1}", work.TimeArea, work.Company); } public object Clone() { Resume obj = new Resume(this.work);//調用私有構造方法,讓工作經歷克隆完成,然后在給這個簡歷對象的相關字段賦值,最終返回一個深拷貝的對象 obj.name = this.name; obj.sex = this.sex; obj.age = this.age; return obj; } }
運行結果:

六、優缺點及適用場景
優點:
1)原型模式向客戶隱藏了創建新實例的復制性
2)允許動態增加或者減少產品類。
缺點:
1)每個類必須配備一個克隆方法
2)對于新建的類使用拷貝很容易,對于已有的類進行拷貝時,特別當一個類引用不支持串行化的間接對象或引用含有循環結構的時候。
適用場景:
原型模式是在內存二進制流的拷貝,比直接new一個對象性能好很多,特別是需要大量對象時,可以考慮使用原型模式。
類實例化需要消耗非常多的資源時或者實例化需要復雜的數據準備訪問權限時。
新聞熱點
疑難解答