1. 程式人生 > >用心理解設計模式——原型模式 (Prototype Pattern)

用心理解設計模式——原型模式 (Prototype Pattern)

前置文章: 用心理解設計模式——設計模式的原則 

設計模式相關程式碼已統一放至 我的 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
        }
    }
}