原始模型(Prototype)模式属于对象的创建模式。
通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这就是原始模型模式的用意。
Java对象的复制
Java的所有类都是从 java.lang.Object 类继承而来的,而 Object 类提供下面的方法对对象进行复制:
protected Object clone()
子类也可以把这个方法置换掉,提供满足自己需要的复制方法。
Java语言提供的Cloneable接口只有一个作用,就是在运行时期通知Java虚拟机可以安全的在这个类上使用 clone() 方法。
由于Object 类本身并不实现 Cloneable 接口,因此如果所考虑的类没有实现Cloneable接口时,调用 clone() 方法会抛出 CloneNotSupportedException 异常。
克隆满足的条件
一般而言, clone() 方法满足以下描述:
- 对任何的对象 x , 都有: x.clone() != x 。 也就是说,克隆对象与原对象不是同一个对象。
- 对任何的对象 x , 都有: x.clone().getClass == x.getClass(), 也就是说克隆对象与原对象的类型一样。
- 如果对象的 x 的 equals()方法定义恰当的话,x.clone().equals(x) 应当是成立的。
简单形式的原始模型模式
涉及三个角色:
- 客户角色(Client):客户类提出创建对象的请求。
- 抽象原型角色(Prototype): 这是一个抽象角色,通常由一个Java接口或Java抽象类实现。此角色给出所有具体原型类所需的接口。
- 具体原型角色(Concrete Prototype): 被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
示意性源码
登记形式的原始模型模式
角色如下:
- 客户端角色(Client): 客户端类向管理员提出创建对象的请求。
- 抽象原型角色(Prototype):此角色给出所有具体原型类所需的接口。
- 具体原型角色(Concrete Prototype): 被复制的对象。需要实现抽象的原型角色所要求的接口。
- 原型管理器角色(Prototype Manager): 创建具体原型类的对象,并记录每一个被创建的对象
两种形式的比较
如果需要创建的原型对象数目较少而且比较固定,可以采用第一种形式,即简单形式的原始模型模式。这种模式中,原型对象的引用可以由客户端自己保存。
如果要创建的原型对象数目不固定的话,可以采用第二种形式,即登记形式的原始模型模式。这种模式中,由管理员保存原型对象的引用。
模式的实现:深复制和浅复制
浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。
深复制要深入到多少层,是一个不易确定的问题。在决定以深复制的方式复制一个对象的时候,必须决定对间接复制的对象是采取浅复制还是继续采取深复制。因此,在采取深复制时,需要决定多深才算深。此外,在深复制的过程中,很可能会出现循环引用的问题,必须小心处理。
利用串行化来做深复制
把对象写到流里的过程是串行化(Serilization)过程;而把对象从流中读出来过程叫做并行化过程。
应当指出的是,写到流里的是对象的拷贝,而原对象仍然存在于JVM里面。
在java语言里深复制一个对象,常常可以先使对象实现 Serialization 接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读回来,便可以重建对象。
深复制的源代码
这样做的前提就是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象是否可以设成 transient, 从而将之排除在复制过程之外。
在什么情况下使用原始模型模式
假设一个系统的产品类是动态加载的,而且产品类具有一定的等级结构。这个时候如果采取工厂模式的话,工厂类就不得不具有一个相应的等级结构。而产品类的等级结构一旦变化,工厂类的的等级结构就不得不有一个相应的变化。这对于产品结构可能会有经常性变化的系统来说,采用工厂模式就有不方便之处。
这时如果采用原始模型模式,给每一个产品类配备一个克隆方法(大多数时候只需要给产品类等级结构的根类配备一个克隆方法),便可以避免使用工厂模式所带来的具有固化等级结构的工厂类。
这样,一个使用了原始模型模式的系统与它的产品对象是怎么创建出来的,以及这些产品对象之间的结构是怎样的,以及这些结构会不会发生变化时没有关系的。
原始模型模式的优点和缺点
抽象工厂模式有许多与原始模型模式和建造模式相同的效果,包括客户端不知道具体产品类,而只知道抽象产品类,客户端不需要知道这么多的具体产品名称。如果有新的产品类加入,客户端不需要进行改造就可直接使用。
原始模型模式有其特有的优点:
- 原始模型模式允许动态的增加或减少产品类。由于创建产品类实例的方法是产品类内部具有的,因此,增加新产品对整个结构没有影响。
- 原始原型模式提供简化的创建结构。
- 具有给一个应用软件动态加载新功能的结构。
- 产品类不需要非得有任何事先确定的等级结构,因为原始模型模式适用于任何的等级结构。
原始模型模式的最主要的缺点是每一个类都必须配备一个克隆方法。
配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难。
而对于已经有的类不一定很容易,特别是当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。