一、为何需要原型模式(Prototype Pattern)?
在软件设计中,我们会遇到到这样的情况:对原对象进行拷贝一个新的副本。想要实现这样的逻辑,有一种笨方法就是对原对象里的所有变量进行逐一赋值。但是这样的做法会导致代码臃肿,逻辑复杂,还可能会有遗漏某些变量没有被赋值的问题。
为了解决这个在代码中对每一个变量进行赋值的问题,同时又保证能够实现拷贝的功能,于是原型模式就出来了。
定义: 用一个已经创建的实例作为原型,来创建一个与原型对象相同的新对象
【角色】
- 抽象原型类: 规定具体原型对象必须实现的clone()方法
- 实现原型类: 实现抽象原型类的clone()方法。
原型模式分为浅克隆和深克隆
浅克隆(浅拷贝):创建一个新的对象,新对象的属性和原型对象的完全相同。对对象里的引用类型成员,仍指向原来所指向的内存地址。(这里属性指的是:值类型。同下)
深克隆(深拷贝):创建一个新对象,对对象里的属性和引用类型成员都会被克隆,引用类型成员不再指向原来的地址。
应用条件:
- 对象里有非常多成员,尤其该对象的引用类型成员里还有其他的引用类型对象。
二、例子
需求:
假设有一盏灯A,然后另一盏灯B根据A灯的样子造出来的。可以说这两盏灯外观一摸一样。在黑暗中,要想实现一个能有照明系统的用途,这两盏灯还需要连接一块电池才能发光照明。
假定电池为引用类型成员,A灯和B灯属于值类型成员。
A灯+电池 = 原对象。
B灯 +电池 = 新对象。
现有两种情况可以选择:
1)情况(浅拷贝):为了节约电池的成本,把这两盏灯都连接在同一块电池。
2)情况(深拷贝):为了这两盏灯要完全分开互不影响,一盏灯连接一块电池,所以需要两块容量一致的电池。
Serializable: 序列化,只对深拷贝有作用。系统遍历原对象里所有引用类型成员进行逐一克隆,无需代码来处理具体的过程。被标记为 Serializable 的类才能够使用深拷贝机制,否则程序运行会崩溃。
c
//引用类型:电池类
[Serializable]
public class Battery
{
public int Capacity;
}
//灯的原型抽象类
[Serializable]
public abstract class LampPrototype
{
public abstract int Use();
public abstract LampPrototype ShallowClone();//浅拷贝
public abstract LampPrototype DeepClone();//深拷贝
}
//灯的实现类
[Serializable]
public class Lamp : LampPrototype
{
//引用类型,电池
public Battery battery = new Battery();
public override int Use(){
return battery.Capacity;
}
//浅拷贝
public override LampPrototype ShallowClone(){
return (Lamp)this.MemberwiseClone();
}
//深拷贝
public override LampPrototype DeepClone(){
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return (Lamp)formatter.Deserialize(stream);
}
}
class Program
{
//设置原对象的数据
public static void setOriginal(LampPrototype lampPro) {
var lamp = lampPro as Lamp;
lamp.battery.Capacity = 100;
}
//设置新对象的数据
public static void setNew(LampPrototype lampPro) {
var lamp = lampPro as Lamp;
lamp.battery.Capacity = 80;
Console.WriteLine("New Capacity::80");
}
static void Main(string[] args)
{
//原对象
LampPrototype Originallamp = new Lamp();
setOriginal(Originallamp);
//浅拷贝
Console.WriteLine();
Console.WriteLine("After ShallowCopy,Original:");
LampPrototype shallowAudio = Originallamp.ShallowClone();
setNew(shallowAudio);//新对象使用电池,原对象的电池容量受到影响
Console.WriteLine("Original Capacity:" + Originallamp.Use());
//恢复原对象数据
setOriginal(Originallamp);
//深拷贝
Console.WriteLine();
Console.WriteLine("After DeepCopy,Original:");
LampPrototype deepAudio = Originallamp.DeepClone();
setNew(deepAudio);//新对象使用电池,原对象的电池容量没有受到影响
Console.WriteLine("Original Capacity:" + Originallamp.Use());
Console.ReadLine();
}
}