用心理解設計模式——原型模式 (Prototype Pattern)
阿新 • • 發佈:2018-11-21
前置文章: 用心理解設計模式——設計模式的原則
設計模式相關程式碼已統一放至 我的 Github
一、定義
建造型模式之一。
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
(用原型例項指定要建立物件的種類,並通過拷貝此原型建立新的物件)
二、結構解析
原型模式的一般結構有兩種角色:抽象原型、具體原型。
抽象原型類,宣告Clone自身的介面方法;
具體原型類,實現Clone自身的介面方法。
三、評價
原型模式比較簡單。它提供了一個克隆自身的介面方法, 讓一些具體類去實現自身的克隆操作,這樣就可以通過具體類生成自身的克隆例項。
需要藉助 C# Object物件的MemberwiseClone方法。注意它是淺拷貝的。
注意區分深拷貝和淺拷貝:
淺拷貝,基礎型別按位複製(產生新的成員變數),引用型別建立一個新的引用,並讓這個引用指向原引用指向的物件。
深拷貝,基礎型別按位複製,引用型別以該引用指向的物件的值為構造引數構造一個新的物件,並建立一個新的引用指向這個心的物件。
注意 string型別的特殊性! 它雖然不是基礎型別,但具有基礎型別的性質。這個之後有時間要另開一篇文章整理。
四、實現
using System; using UnityEngine; namespace Prototype { //輔助測試成員類 public class Extra { public int id; public string name; public Extra(int id, string name) { this.id = id; this.name = name; } } //抽象原型,提供抽象的拷貝介面方法 public abstract class Prototype { public abstract Prototype ShallowClone(); public abstract Prototype DeepClone(); } //具體原型,實現拷貝方法 public class ConcretePrototype : Prototype { public int id = 1; public string name = "A"; public Extra extra = new Extra(1, "A"); public override Prototype ShallowClone() { return (ConcretePrototype)this.MemberwiseClone(); } public override Prototype DeepClone() { //先淺拷貝 ConcretePrototype p = (ConcretePrototype)this.MemberwiseClone(); //再處理引用型別成員新建處理。 以當前成員新new出來,或 在該引用型別中也實現深拷貝方法,然後呼叫。 p.extra = new Extra(this.id, this.name); //這句其實可以不需要,因為雖然String不是基礎成員,但因為其為靜態常量,所以具有基礎成員的性質。 p.name = String.Copy(this.name); return p; } } public class Client { static public void Main() { ShallowCloneTest(); Debug.Log("---------------------------------"); DeepCloneTest(); } static private void ShallowCloneTest() { //建立原型 a, 並淺拷貝為 b。 ConcretePrototype a = new ConcretePrototype(); ConcretePrototype b = (ConcretePrototype)a.ShallowClone(); Debug.Log(a.id + ", " + a.name + ", " + a.extra.id + ", " + a.extra.name); //1, A, 1, A Debug.Log(b.id + ", " + b.name + ", " + b.extra.id + ", " + b.extra.name); //1, A, 1, A //嘗試修改b b.id = 2; b.name = "B"; b.extra.id = 2; b.extra.name = "B"; //B正常,全部被修改 //A的基礎型別成員(string不是基礎成員,但具有基礎成員的性質)沒有被修改(正常)。但引用型別成員被修改(因為淺拷貝的緣故) Debug.Log(a.id + ", " + a.name + ", " + a.extra.id + ", " + a.extra.name); //1, A, 2, B Debug.Log(b.id + ", " + b.name + ", " + b.extra.id + ", " + b.extra.name); //2, B, 2, B } static private void DeepCloneTest() { //建立原型 a, 並淺拷貝為 b。 ConcretePrototype a = new ConcretePrototype(); ConcretePrototype b = (ConcretePrototype)a.DeepClone(); Debug.Log(a.id + ", " + a.name + ", " + a.extra.id + ", " + a.extra.name); //1, A, 1, A Debug.Log(b.id + ", " + b.name + ", " + b.extra.id + ", " + b.extra.name); //1, A, 1, A //嘗試修改b b.id = 2; b.name = "B"; b.extra.id = 2; b.extra.name = "B"; //B正常,全部被修改。 //A正常,沒有因為B的修改而被修改。 Debug.Log(a.id + ", " + a.name + ", " + a.extra.id + ", " + a.extra.name); //1, A, 1, A Debug.Log(b.id + ", " + b.name + ", " + b.extra.id + ", " + b.extra.name); //2, B, 2, B } } }