原型模式
本篇博客将介绍原型模式,原型模式是一种比较特殊的模式,它通过复制一个已经存在的对象来得到一个全新的对象,原则上我们改变新对象的内容源对象是不会受影响的。学习原型模式我们就需要明白程序中的两种克隆类型:深克隆和浅克隆。
模式分类
创建型设计模式。
模式产生的原因
当我们需要创建一大堆成本开销比较大的对象但是他们都是相似或者相同的时,我们可以考虑可不可以参考一个已经创建好的对象来创建出其余相同或者相似的对象。这样可以提高我们的创建效率。
模式的灵感来源
《西游记》中孙悟空可以拔毛变小猴,孙悟空可以根据自己的形象复制出很多和自己长得一模一样的身外身来。孙悟空这种根据自己的形象复制出身外身的技巧在软件设计中就是原型模式。
事实上在实际的生产中,尤其是零件的生产一般都是需要模型数据的,工人们不可能每个零件都自己去调整,一般都是通过一个模具来快速生产目标零件。
模式类图
经典原型模式包含以下3种角色:
Prototype(抽象原型类):
他是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口,甚至是具体实现类。
ConcretePrototype(具体原型类):
负责实现抽象克隆类中声明的克隆方法并返回克隆对象。
Client(客户类):
在客户端类中,我们会让一个原型对象自身克隆从而创建一个或者多个新对象。
深克隆和浅克隆
根据在复制对象的同时是否同时复制包含在原型对象中的引用类型的对象,克隆分为两种:深克隆和浅克隆。
浅克隆
在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给新对象,如果是引用类型,在浅克隆中会复制一份引用类型的引用给新对象,也就是说新对象和原型对象的引用变量指向相同的内存地址。简单来说,浅克隆只会复制变量中直接存储的内容。
深克隆
深克隆中,如果原型对象的成员变量是值类型,将复制一份给新对象,如果是引用类型,深克隆会将引用变量指向内存区域中存储的实际数据复制一份给新对象,这时新对象的所有操作都不会对原型对象产生影响,真正意义上的全新的对象。
经典代码实现
原型模式通用实现:
Prototype:
namespace Prototype.Prototype.Example
{
public interface IPrototype
{
IPrototype Clone();
}
}
ConcretePrototype:
namespace Prototype.Prototype.Example
{
public class ConcretePrototype : IPrototype
{
public int ParamA;
public string ParamStr;
public IPrototype Clone()
{
ConcretePrototype prototype = new ConcretePrototype();
prototype.ParamA = ParamA;
prototype.ParamStr = ParamStr;
return prototype;
}
}
}
原型模式C#实现:
原型模式使用C#实现时分为浅拷贝和深拷贝两种实现,其中C#官方已经帮我们实现了浅拷贝,深拷贝需要我们自己实现C#提供的ICloneable接口实现。
浅拷贝:
namespace Prototype.Prototype.ExampleCSharp
{
public class Member
{
}
public class ShallowCopyTest
{
public Member Member;
public ShallowCopyTest Clone()
{
return (ShallowCopyTest) this.MemberwiseClone();
}
}
}
深拷贝:
namespace Prototype.Prototype.ExampleCSharp
{
public class Member
{
}
public class DeepCopyTest
{
public Member Member;
public DeepCopyTest Clone()
{
DeepCopyTest deep = (DeepCopyTest) this.MemberwiseClone();
deep.Member = new Member();
return deep;
}
}
}
原型模式总结
原型模式的优点:
- 当我们要创建的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
- 拓展性好,由于原型模式提供了抽象原型类,可以针对抽象原型类进行编程。
- 原型模式的创建过程简单。
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份将其状态保存起来,以便在需要时使用,例如撤销操作。
原型模式的缺点:
- 需要为每个类都配备一个克隆方法,而且克隆方法位于一个类的内部,违背了开闭原则。
- 在实现深克隆是往往要书写较为复杂的逻辑,对于复杂对象的克隆实现可能较为复杂。