老生常谈:原型模式
意图:
通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多的同类型对象。Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节。
Prototype模式分类:
1:简单形式。
结构图如下:
结构说明:
1:客户角色:客户类提出创建对象的请求。
//通过原型模式得到的对象
//些处为浅拷贝
ConcretePrototype _ConcretePrototypeCloned =
(ConcretePrototype)_Prototype.PrototypeClone();
2:抽象原型角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
/// 抽象原型(Prototype)角色
/// 定义了一个克隆方法
/// </summary>
public interface Prototype
{
Prototype PrototypeClone();
}
3:具体原型角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。
/// 具体原型(Concrete Prototype)角色
/// 被克隆对象
/// </summary>
[Serializable ]
public class ConcretePrototype : Prototype
{
/// <summary>
/// 变通属性
/// </summary>
public string sName
{
get;
set;
}
/// <summary>
/// 浅拷贝
/// </summary>
/// <returns></returns>
public Prototype PrototypeClone()
{
//利用Object的受保护方法MemberwiseClone来实现浅拷贝
return (Prototype)this.MemberwiseClone();
}
/// <summary>
/// 深拷贝
/// </summary>
/// <returns></returns>
public Prototype PrototypeDeepClone()
{
//克隆对象
ConcretePrototype _ConcretePrototype;
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
//序列化原始对象
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
//将已经序列化后的原始对象反序列同时赋值给克隆对象
_ConcretePrototype =
(ConcretePrototype)formatter.Deserialize(memoryStream);
return _ConcretePrototype;
}
}
2:登记形式
结构图如下:
结构说明:
1:客户(Client)角色:客户端类向原型管理器提出创建对象的请求。
//通过原型模式得到的对象
//些处为浅拷贝
ConcretePrototype _ConcretePrototypeCloned =
(ConcretePrototype)_Prototype.PrototypeClone();
//实例化原型管理器
PrototyManager<ConcretePrototype> _PrototyManager
= new PrototyManager<ConcretePrototype>();
//将一个复制后的原型加入到原型管理器中
_PrototyManager.add(_ConcretePrototypeCloned);
string sName = _PrototyManager.get(0).sName;
2:抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。和简单方式相同。
3:具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。和简单方式的相同。
4:原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
/// 原型管理器
/// </summary>
/// <typeparam name="T">克隆对象</typeparam>
public class PrototyManager<T>
{
//克隆对象列表
private List<T> _list = new List<T>();
/// <summary>
/// 添加一个克隆对象到原型管理器中
/// </summary>
/// <param name="t"></param>
public void add(T t)
{
_list.Add(t);
}
/// <summary>
/// 读取一个克隆对象
/// </summary>
/// <param name="i">克隆对象在原型管理器中的索引</param>
/// <returns></returns>
public T get(int i)
{
return _list[i];
}
}
C#中对原型模式的应用:
1:Object类的MemberwiseClone()方法来实现对象的浅表拷贝;示例代码参考上面的具体原型角色中的代码。
2:通过序列化的方式来实现深拷贝。示例代码参考上面的具体原型角色中的代码。
3:实现ICloneable接口。 ICloneable接口中有一个Clone方法,可以重写自定义的克隆方法。
浅拷贝和深拷贝:
1:浅拷贝是指被复制对象的所有变量和原始对象的值都相同,而所有对其它对象的引用都仍然指向同一变量,也就是说浅拷贝只拷贝所有的值类型以及所有的引用类型,而所引用的具体对象不拷贝;例如一个原始对象中有一个结构体和一个类字段,只复制结构体和类的引用,克隆对象中的类字段和原始对象中的类字段指向同一对象,它们是共享关系,任何一方对它做修改都会有影响,而结构体则不同,允许克隆对象和原始对象有不同的值。
2:深拷贝是指被复制对象的所有变量和原始对象的值都相同,除去其中所有的引用变量,克隆对象把原始对象中的所有引用对象都拷贝一次,克隆对象中的引用变量和原始对象中的引用变量不指向同一对象,而是指向新创建的对象,深拷贝就好像是重新new的对象一样。
原型与复制对象的关系:
1:复制对象与原始对象不是同一对象,下面的代码运行结果为false;
//结果为false
bool equalsObject = _PrototyManager.get(0).Equals(_ConcretePrototype);
2:复制对象与原始对象的类型相同,下面的代码运行结果为true;
//结果为true
bool equalsType =
_PrototyManager.get(0).GetType().Equals(_ConcretePrototype.GetType());
适用性:
1:客户端不关心他所调用的对象是怎么创建的,内部结构是怎么样时。
2:为了避免创建一个与产品类层次平行的工厂类层次时;(一个工厂类可以有多个工厂方法,创建不同的产品,当一个工厂类只有一个工厂方法,那么这个工厂类 和它的产品的接口就处于一个平行的层次,为了避免创建太多的工厂类,导致结构复杂,可以是用原型模式)。
3:一个系统的产品类会动态加载;
Prototype模式的优点包括
1、Prototype模式允许动态增加或减少产品类。由于创建产品类实例的方法是产批类内部具有的,因此增加新产品对整个结构没有影响。
2、Prototype模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而Prototype模式就不需要这样。
3、Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。
4、产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构。
Prototype模式的缺点:
每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
总结:
本文分析了简单方式和登记形式两种原型模式,在登记形式中的原型管理器中应用泛型解决了拆装箱带来的性能问题。知道了原型模式就很容易明白C#中的深拷贝和浅拷贝的实质。
注:
本文参考:<<Java与模式>>