(一)定义:Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
1.1 UML类图

1.2 类与对象之间的关系
Prototype:抽象原型基类,定义具有克隆自己的方法的接口。
ConcretePrototype:具体原型类,实现具体克隆方法。
Client:客户程序,通过克隆生成一个新的对象。
1.3 典型调用顺序图


代码public abstract class Prototype
{
private string id;
public Prototype(string id)
{
this.id = id;
}
public string ID
{
get
{
return this.id;
}
}
public abstract Prototype Clone();
}
public class ConcretePrototype1 : Prototype
{
private System.Data.DataTable dt = new System.Data.DataTable();
public ConcretePrototype1(string id)
: base(id)
{
}
public override Prototype Clone()
{
return (Prototype)this.MemberwiseClone();
}
}
public class Client
{
ConcretePrototype1 p1 = null;
ConcretePrototype1 c1 = null;
public Client()
{
p1 = new ConcretePrototype1("p1");
c1 = (ConcretePrototype1)p1.Clone();
}
public override string ToString()
{
bool isbool = Object.ReferenceEquals(p1, c1);
return string.Format("p1.id={0} HashCode={1}
c1.id={2} HashCode={3}", p1.ID, p1.GetHashCode(), c1.ID, c1.GetHashCode());
}
}
(二) 原型模式中的浅拷贝和深拷贝(C#语言实现)
2.1 new操作做了什么
new操作符可以创建值类型变量、引用类型对象,同时自动调用构造函数。
例子:假设有一个Point类,有x,y两个变量。以下动画演示了在new操作过程中的内存分配的过程。其中的内存布局图如下:
![456[6] 456[6]](https://images2017.cnblogs.com/blog/329170/201708/329170-20170825160828308-1786831153.gif)

从以上图内存布局图示可以看到p中存储的是一个指向Point对象的地址值。当调用new操作符时会在内存中分配连续地址来建立Point类的内存空间。然后将首地址空间赋值给指向的变量。
假定现有一个Rect类,由两个点组成。

代码public class Point
{
public int x;
public int y;
public Point()
{
}
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
public class Rect
{
public Point startPoint = null;
public Point endPoint = null;
public Rect()
{
startPoint = new Point(0, 0);
endPoint = new Point(10, 10);
}
}
其内存变化如下图:

2.2 程序运行的内存区域
一个程序将操作系统分配给其运行的内存块分为4个区域:
1)代码区:存放程序的代码,即程序中的各个函数代码块。
2)全局数据区:存放程序的全局数据和静态数据。
3)堆区:存放程序的动态数据。在C/C++中,用alloc系统函数和new申请的内存都存在于堆中。在C#语言中有堆和托管堆的区别。
4)栈区:存放程序的局部数据,即各个函数中的数据。
2.3 Object.MemberwiseClone()方法的使用
MemberwiseClone()方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
例如,考虑一个名为 X 的对象,该对象引用对象 A 和 B。对象 B 又引用对象 C。X 的浅表副本创建一个新对象 X2,该对象也引用对象 A 和 B。与此相对照,X 的深层副本创建一个新对象 X2,该对象引用新对象 A2 和 B2,它们分别是 A 和 B 的副本。B2 又引用新对象 C2,C2 是 C 的副本。使用实现 ICloneable 接口的类执行对象的浅表或深层复制。

浅表拷贝代码public class X
{
public A a = null;
public B b = null;
public X()
{
a = new A();
b = new B();
}
public X Clone()
{
return (X)this.MemberwiseClone();
}
}
public class A
{
public A()
{
}
}
public class B
{
public C c = null;
public B()
{
c = new C();
}
}
public class C
{
public C()
{
}
}


2.4 深拷贝的几种实现方式
2.4.1:利用MemberwiseClone()方法特性(.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。)

代码演示public class X : ICloneable
{
public A a = null;
public B b = null;
public X()
{
a = new A();
b = new B();
}
private X(A a, B b)
{
this.a = (A)a.Clone();
this.b = (B)b.Clone();
}
public object Clone()
{
//浅拷贝
//return this.MemberwiseClone();
//深拷贝
X x = new X(this.a, this.b);
return x;
}
}
public class A : ICloneable
{
private string a = "";
public A()
{
}
public string PA
{
get { return this.a; }
set { this.a = value; }
}
public object Clone()
{
return this.MemberwiseClone();
}
}
public class B : ICloneable
{
private string b = "";
public C c = null;
public B()
{
c = new C();
}
public string PB
{
get { return this.b; }
set { this.b = value; }
}
public object Clone()
{
return this.MemberwiseClone();
}
}
public class C : ICloneable
{
private string c = "";
public C()
{
}
public string PC
{
get { return this.c; }
set { this.c = value; }
}
public object Clone()
{
return this.MemberwiseClone();
}
}
代码调试动画分析:从代码调试看出:实现Clone方法后创建得到的对象a中存储的字段值还是“123”当再次赋值为“456”时x2对象的字段值改变,没有影响到x1对象的“123”值。以上代码存在的问题是如果此时对C类的字段c进行赋值改变会同时影响到x1,x2对象的值(如右图)
只需在B中添加和修改以下代码即可:(.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。)
以上方式存在的问题:如果类间的依赖关系太复杂,则每个与之相关的类成员变量都需要实现Clone方法进行非值类型的深拷贝。

代码private B(C c)
{
this.c = (C)c.Clone();
}
public object Clone()
{
//B b = new B(c);
return new B(c);
//return this.MemberwiseClone();
}

2.4.2:利用反射原理实现深拷贝
2.4.3