用心理解设计模式——原型模式 (Prototype Pattern)

前置文章: 设计模式的原则 

其他设计模式:用心理解设计模式专栏

设计模式相关代码已统一放至 我的 Github

 

一、定义

  建造型模式之一。

  Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

  (用原型实例指定要创建对象的种类,并通过拷贝此原型创建新的对象)

二、结构解析

  原型模式的一般结构有两种角色:抽象原型、具体原型。

  抽象原型类(Prototype ):声明Clone自身的接口方法;

  具体原型类(ConcretePrototype ):实现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
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NRatel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值