设计模式之原型模式 

原型模式是通过现有的一个对象,复制出更多的此类型的对象以供使用。

例子:
孙悟空的猴毛:孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来。
红警中的箱子:在玩红警时会出现一些小箱子(魔法箱子),有的小箱子被一辆坦克撞开的时候会变出一辆同样的坦克。

在C#中实现 原型模式很简单,在ICloneable接口中有一个Clone的方法,只要实现该接口就可以通过重写Clone方法来实现对象的拷贝。
原型模式中的拷贝分为“浅拷贝”和“深拷贝”:
“浅拷贝”:对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象。
“深拷贝”:对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。

 

//抽象原型,实现了ICloneable接口,为“具体原型”类接供了实现规则
abstract class ProtoType:ICloneable
{
private string s;
public ProtoType(string str)
{
this.s = str;
}
// 显示成员变量的信息
public void show()
{
Console.WriteLine(s);
}
//未实现ICloneable接口中的Clone方法,留给“具体原型”类来实现
public abstract object Clone();
}
//具体原型,即要被复制的对象的类,它继承子抽象原型。
class ConcreteProtoType : ProtoType
{
public ConcreteProtoType(string str)
: base(str)
{ }
//实现了“抽象原型”中未实现的Clone方法
public override object Clone()
{
//调用Object类中的MemberwiseClone方法实现浅拷贝。
return (ProtoType)this.MemberwiseClone();
}
}
//客户程序
public class Client
{
public static void Main()
{
ConcreteProtoType p1 = new ConcreteProtoType("hello world");
//实现对p1的拷贝
ConcreteProtoType p2 = (ConcreteProtoType)p1.Clone();
p1.show();
p2.show();
}
}

深拷贝与浅拷贝:
浅拷贝:只需在“具体原型”类的Clone方法中执行return this.MemberwiseClone();就可以实现对象的浅拷贝,如上例。
深拷贝:需要在“具体原型”类的Clone方法中实例化一个新的“具体原型”对象,并把当前对象成员变量的值赋给新生成的对象成员变量
//深拷贝
class DeepCopy : ICloneable
{
private List<string> array = new List<string>();
public DeepCopy(params string[] str)
{
foreach (string s in str)
{
array.Add(s);
}
}
//改变成员变量,以验证是否真的实现了深拷贝。
public void Add(string s)
{
array.Add(s);
}
public object Clone()
{
//新实例化一个DeepCopy对象,并在构造函数中对成员变量被始化。
return new DeepCopy(array.ToArray());
}
//显示当前对象的成员变量的内容
public void Show()
{
Console.WriteLine("======DeepCopy======");
foreach (string s in array)
{
Console.WriteLine(s);
}
}
}
class Client
{
public static void Main(string[] args)
{
DeepCopy dc = new DeepCopy("hello", "world");
//调用dc的Clone方法进行拷贝,此时的拷贝是深拷贝。
DeepCopy dd = (DeepCopy)dc.Clone();
dc.Add("Hello C#");
dc.Show();
dd.Show();
}
}

一个完整例子:
该例子演示了红警中“魔法箱子”中的原型模式的实现,引例子中注释的两行内容为浅拷贝的代码。
//抽象原型类,是所有车辆的父类,实现了ICloneable接口。
abstract class Vehicle : ICloneable
{
protected Hashtable info; //存储车辆的信息(RGB颜色值和生命)
public Vehicle(int red, int green, int blue, int blood)
{
info = new Hashtable();
info.Add("red", red);
info.Add("green", green);
info.Add("blue", blue);
info.Add("blood", blood);
}
//显示车辆成员变量中的信息
public virtual void Show()
{
Console.WriteLine("======The Vehicle Property======");
foreach (DictionaryEntry de in info)
{
Console.WriteLine(de.Key + ":" + de.Value);
}
}
//当车辆受到攻击时,减少生命值的方法。
public void Attacked(int bloodloose)
{
info["blood"] = (int)info["blood"] - bloodloose;
}
//有待于“抽象原型”实现的方法
public abstract object Clone();
}
//“具体原型”长角坦克
class HornTank : Vehicle
{
//坦克的类型名称
private const string type = "horn tank";
public HornTank(int red, int green, int blue, int blood)
: base(red, green, blue, blood)
{ }
public override void Show()
{
base.Show();
Console.WriteLine(type);
}
public override object Clone()
{
//实现对长角坦克的深拷贝
return new HornTank((int)info["red"], (int)info["green"], (int)info["blue"], (int)info["blood"]);
//对长角坦克的浅拷贝
//return this.MemberwiseClone();
}
}
//“具体原型”轻坦克
class LightTank : Vehicle
{
//坦克的类型名称
private const string TYPE = "light tank";
public LightTank(int red, int green, int blue, int blood)
: base(red, green, blue, blood)
{ }
public override void Show()
{
base.Show();
Console.WriteLine(TYPE);
}
public override object Clone()
{
//实现对轻坦克的深拷贝
return new LightTank((int)info["red"], (int)info["green"], (int)info["blue"], (int)info["blood"]);
//实现对轻坦克的浅拷贝
//return this.MemberwiseClone();
}
}
//魔法箱子类,可以生成一辆同样的新坦克
class MagicBox
{
public Vehicle Open(Vehicle v)
{
//根据v生成一个新的对象。
return (Vehicle)v.Clone();
}
}
class Client
{
public static void Main(string[] args)
{
//一辆轻坦克
LightTank lt = new LightTank(100, 200, 150, 100);
//该坦克被打掉46点生命值,剩下54点生命值。
lt.Attacked(46);
//一个魔法箱子
MagicBox mb = new MagicBox();
//魔法箱子打开时深拷贝一个同样的轻坦克,新的轻坦克也只有54点生命值。
Vehicle ve = mb.Open(lt);
//拷贝出的轻坦克 被打掉23点生命值,由于进行的是深拷贝,原轻坦克的生命值并未受影响。
ve.Attacked(23);
lt.Show();
ve.Show();
}
}
运行结果:

带Prototype Manager的原型模式

 

Prototype Manager用来管理要被创建的对象。实现对多个“具体原型”的统一管理。

有人问直接用new 运算符实例化一个类的对就得了,为什么非要这么麻烦地实现拷贝?
其实,直接使用new运算符和原型模式都可以创建出一个对象。而原型模式实际上是动态抽取当前对象运行时的状态,用当前对象的状态来生成新的对象实例。(车延禄)

在以下几种情况中应当采用原型模式来创建对象:
1、对象的构造函数非常复杂,在生成新对象的时候非常耗时间、耗资源的情况,使用原型模式可以简捷地创建新对象。
2、程序运行过程中,由于一些数据随进都在变化,事后很难使用构造函数再构造出一个和原来一样的对象。
3、不知道当前对象的状态,即目前不知道当前对象成员变量的值都变成了什么内容,故无法再用构造函数来构造和当前对象一样的对象了。

你可能感兴趣的:(设计模式,prototype)