意图
用原型实例来指定要创建对象的类型,并且通过拷贝这些原型创建新的对象。
动机
背景
有两个类系统:一个是GraphicTool,一个是Graphic及其子类。
问题
GraphicTool要创建Graphic特定子类,却不知道如何创建,因为此时Graphic特定子类可能还未定义。
解决方法
为所有Graphic子类都提供一个Clone操作。在GraphicTool要创建Graphic特定子类的时候,传入一个创建好的Graphic特定子类对象,GraphicTool只需要调用此对象的Clone操作就可以获得Graphic特定子类对象。
改进
甚至可以不创建Graphic子类,只需要使用Graphic类来创建出几个我们原来只有通过先定义Graphic子类才能创建的对象,这样可减少类的数目。
示例图形
适用性
- 当一个系统应该独立于它的产品创建、构成和表示时。
- 当要实例化的类是在运行时刻指定时。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。(建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。)
结构
参与者
- Prototype:声明一个克隆自身的接口。
- ConcretePrototype:实现一个克隆自身的操作。
- Client:让一个原型克隆自身从而创建一个新的对象。
协作
客户请求一个原型克隆自身。
效果
- 与(3.1)Abstract Factory、(3.2)Builder一样:对用户隐藏具体产品类,因此减少了客户知道的名字的数目。
- 运行时刻增删产品。Prototype比其他模式更加灵活,因为客户很容易在运行时刻建立和删除原型。
- 改变值以指定新对象。高度动态的系统使你可以为一个对象变量指定新值,从而改变行为。这有利于大大减少系统所需要的类的数目。
- 改变结构以指定新对象。有些对象是由部件和子部件构成的,这种复合对象只要将Clone实现为一个深拷贝也能应用Ptototype模式。
- 减少子类的构造。相比与(3.3)Factory Method,Ptototype不需要产生一个与产品类层次平行的Creator类层次,减少了类的数目。C++从中获益较大,因为C++是不将类作为一级类对象的语言。(即C++不像Smalltalk和Objective-C,不能将类当做对象用,也不能将对象当做类用。)
- 用类动态配置应用。使用一个原型管理器来动态将类装载到应用中。
- 大量使用(4.3)Composite和(4.4)Decorator模式的设计通常也可从Prototype模式处获益。
- 缺陷:每一个Prototype的子类都需要实现Clone操作,这可能很困难:
- 类已经存在时,难以新增Clone操作。(需要修改接口)
- 类内部包括一些不支持拷贝或循环引用的对象时,实现Clone可能也会很困难的。
实现
- 某些语言天生支持Prototype模式,如Self,因为它们所有对象的创建都是通过克隆一个原型实现的。
- 使用原型管理器。当一个系统中原型数目不固定时,要维护一个原型管理器(关联存储器)。客户可以在管理器中存储、删除、检索原型。
- 实现克隆操作。Prototype模式最困难的部分在于正确实现Clone操作。你要决定使用浅拷贝还是深拷贝。
- 初始化克隆对象。对象克隆完成后,可能需要初始化一部分属性。此时不可以通过给Clone操作传参数来完成,因为这样会破坏接口。最好的选择是再实现一个Initialize操作,在克隆完成后自行此操作。