前置文章: 用心理解设计模式——设计模式的原则
设计模式相关代码已统一放至 我的 Github
一、定义
建造型模式之一。
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
(用原型实例指定要创建对象的种类,并通过拷贝此原型创建新的对象)
二、结构解析
原型模式的一般结构有两种角色:抽象原型、具体原型。
抽象原型类,声明Clone自身的接口方法;
具体原型类,实现Clone自身的接口方法。
扫描二维码关注公众号,回复:
4174666 查看本文章
三、评价
原型模式比较简单。它提供了一个克隆自身的接口方法, 让一些具体类去实现自身的克隆操作,这样就可以通过具体类生成自身的克隆实例。
需要借助 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
}
}
}