文章目录
原型(Prototype)模式
隶属类别——对象创建型模式
1. 意图
用原型实例创建对象的种类,并且通过拷贝这些原型创建新的对象。
2. 别名
无
3. 动机
你可以通过定制一个通用的图形编辑器框架和增加一些表示音符、休止符和五线谱的新对象来构造一个乐谱编辑器。这个编译器框架可能有有一个工具选择板用于将这些音乐对象加到乐谱中。这个选择板可能还包括选择、移动和其他操纵音乐对象的工具。用户可以点击四分音符工具并使用它将四分音符加到乐谱中。或者他们可以使用移动工具在五线谱上上下移动一个音符,从而改变它的音调。
我们假定该框架为音符和五线谱这样的图像构件提供了一个抽象的Graphics类。此外,为定义选择板中的那些工具,还提供了一个抽象类Tool。该框架还为一些创建图像对象实例并它们加入到文档中的工具预定义了一个GraphicTool子类。
但GraphicTool给框架设计者带来一个问题。音符和五线谱的类特定于我们的应用,而GraphicTool类却属于框架。GraphicTool不知道创建我们的音乐类的实例,并将我们添加到乐谱中。我们可以为每一种音乐对象创建一个GraphicTool子类,但这样会产生大量的子类,这些子类仅仅在它们所初始化的音乐对象的类别上有所不同。我们知道对象复合是创建子类更灵活的一种选择。问题是,该框架怎么样用它来参数化GraphicTool的实例,而这些实例是由Graphic类所支持创建的。
解决办法是让GraphicTool通过拷贝或者“克隆”一个Graphic子类的实例来创建新的Graphic,我们称这个实例为一个原型。GraphicTool将它应该克隆和添加到文档中的原型作为参数。如果所有Graphic子类都支持一个Clone操作,那么GraphicTool可以克隆所有种类的Graphic,如下图所示:
因此在我们的音乐编辑器中,用于创建个音乐对象的每一种工具都是一个用不同原型进行初始化的GraphicTool的实例。通过克隆一个音乐对象的原型并将这个克隆添加到乐谱中,每个GraphicTool实例都会产生一个音乐对象。
我们甚至可以进一步使用prototype模式来减少类的数量。我们使用不同的类来表示全音符合半音符,但可能不需要这么做。它们可以是使用不同位图和延时初始化相同的类的实例。一个创建全音符的工具就是这样的GraphicTool,它的原型是一个被初始化称全音符的MusicalNote。这可以极大的减少系统中的类的数目,同时也更易于在音乐编辑器中添加新的音符。
4. 适用性
在以下情况可以考虑使用prototype模式:
- 当一个系统独立于它的产品创建、构成和表示时,要使用Prototype模式;
- 当要实例化的类是在运行时可开指定时,例如,通过动态装载;
- 为了避免创建一个与产品类层次平行的工厂类层次时;
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相同数目的原型并克隆它们可能比每次用何时的状态手动实例化该类更方便一些。
5. 结构
6. 参与者
- Prototype(Graphic)
- 声明一个克隆自身的接口
- ConcretePrototype(Staff、WholeNote、HalfNote)
- 实现一个克隆自身的操作
- Client(GraphicTool)
- 让一个原型克隆自身从而创建一个新的对象。
7. 协作
- 客户请求一个原型克隆自身。
8. 效果
Prototype有许多和Abstract Factory和Builder一样的效果:它对客户隐藏了具体的产品类,因此减少了客户知道的类的数目,此外,这些模式使客户无需改变即可使用与特定应用相关的类。
下面列出prototype的另外一些优点:
-
- 运行时刻增加和删除产品 Prototype允许只通过客户注册原型实例就可以将一个新的具体产品类加并入系统。它比其它创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
-
-
改变值以指定新对象 高度动态的系统允许你通过对象复合定义新的行为——例如,通过为一个对象变量指定值——并且不定义新的类。你通过实例化已有类并且将这些实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。
这种设计使得用户可以无需编程即可定义新“类”。实际上,克隆一个原型类似实例化一个类。Prototype模式可以极大的减少系统所需要的类的数目。在我们的音乐编辑器中,一个GraphicTool类可以创建无数音乐对象。
-
-
-
改变结构以指定新对象 许多应用由部件和子部件来创建对象。例如电路设计编辑器就是由子电路来构造电路的。为方便起见,这样的应用通常允许你实例化复杂的、用户指定的结构,比方说,一次又一次的重复使用一个特定的子电路。
Prototype模式也支持这一点。我们仅需将这个子电路作为一个原型增加到可用的电路元素选择板中。只要复合电路对象将Clone实现为一个深拷贝(deep copy),具有不同结构的电路就可以是原型了。
-
-
- 减少子类的构造 Factory Method 经常产生一个与产品类层次平行的Creator类层次。Prototype模式使得你克隆一个原型而不是请求一个工厂方法去产生一个新的对象。因为你根本不需要的Creator类层次。这一优点主要适用于像C++这样不将类作为一级类对象的语言。像Smalltalk和Objective C 这样的语言从中获益较少,因为你总是可以用一个类对象作为生成这。在这些语言中,类对象已经起到了原型一样的作用了。
-
-
用类动态配置应用 一些运行时刻环境允许你动态将类装载到应用中。在像C++这样的语言中,Prototype模式时利用这种功能的关键。
一个希望创建动态载入类的实例的应用不能静态引用类的构造器。而应该由运行环境在载入是自动创建每个类的实例,并用原型管理器来注册这个实例(参见实现一节)。这样应用就可以向原型管理器请求新装载的类的实例,这些类原本并没有和程序向连接。
-
Prototype的缺点:
-
- 每一个Prototype子类都要实现Clone操作 这可能很困难,还好吧,Java中Cloneable接口,实现这个接口就可以了。当内部包括一些不支持拷贝