原型模式(Prototype)

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

 

类图

Prototype

原型模式涉及到三个角色:

  • 客户端(Client)角色:客户端类提出创建对象的请求。
  • 抽象原型(Protorype)角色:这是一个抽象角色,通常由一个Java接口或抽象类实现。此角色给出所有具体原型类所需的接口,
  • 具体原型(ConcretePrototype)角色:被复制的对象。此角色需要实现抽象原型角色所要求的接口。

 

代码实现

简单形式

Client类

 Prototype接口
 ConcretePrototype类

登记形式

如果客户端角色所使用的原型对象只有固定的几个,那么使用单独的变量来引用每一个原型对象就很方便,这时候简单形式的原型模式就比较合适。而如果客户端所创建的原型的对象个数不是固定的,而是动态变化的,那么就不妨创建一个原型管理器类,作为对所有原型对象的登记,这个角色提供必要的方法,供外界增加新的原型对象和取得已经登记过的原型。

PrototypeManager类

Client类

 

复制的实现

clone() 方法将对象复制了一份并返还给调用者。 clone() 方法应满足以下描述:

  1. 对任何对象x ,都有: x.clone() != x 。换而言之,复制对象与原始对象不是同一个对象。
  2. 对任何对象x ,都有: x.clone().gerClass() == x.getClass() 。换而言之,复制对象与原始对象的类型一样。
  3. 如果对象x的 equals() 方法定义恰当的话,那么 x.clone().equals(x) 应当为true。

 

浅复制

复制对象的所有变量都含有与原来的对象相同的值,二所有的对其他对象的引用都仍然指向原来的对象。换而言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。浅复制很容易实现,因为Java语言的所有类都会继承 clone() 方法,而这个 clone() 方法所做的正是浅复制。

深复制

除去那些引用其他对象的变量,复制对象的所有变量都含有与原来的对象相同的值。那些引用其他对象的变量将指向被复制过的新对象。换而言之,深复制把要复制的对象所引用的对象都复制了一遍,而这种对被引用对象的复制叫做间接复制。

深复制要深入到多少层是一个不易确定的问题。在决定以深复制的方式复制一个对象时,必须决定对间接复制的对象采取浅复制还是继续采用深复制。因此,在采用深复制时,需要决定多深。此外,在深复制的过程中,很可能出现循环引用的问题,必须小心处理。

深复制的实现可以利用串行化。把对象写到流的过程是串行化(Serilization)过程,把对象从留中读出来的过程是并行化(Deserialization)过程。应当指出的是,写到流里的是对象的一个拷贝,而源对象仍然存在于JVM里面。

在Java语言里深复制一个对象,常常可以先使对象实现Serilization接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读回来,便可以重建对象。

这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成 transient ,从而将至排除在复制过程之外;或者由程序自行创建出相当的同种对象,全当做复制件使用。比如线程(thread)对象或者Socket对象。

 

使用场合

假设一个系统的产品类是动态加载得,而产品类具有一定的等级结构。这个时候如果采用工厂模式,工厂类就不得不具有一个相应的等级结构。而产品类的等级结构一旦变化,工厂类的等级结构就不得不有一个相应的变化,这对于产品结构可能会有经常性变化的系统来说,采用工厂模式就有不方便之处。这时候可以使用原型模式,给每一个产品类配备一个克隆方法(通常只需给根类配备),这样可以克服工厂模式的缺点。

使用了原型模式的系统与它的产品对象是怎么创建出来的,以及这些对象之间的结构时怎么样的,以及这个结构会不会发生变化是没有关系的。

 

优点

  • 允许动态地增加或减少产品类。
  • 提供简化的创建结构。
  • 具有给一个应用软件动态加载新功能的能力。
  • 产品类不需要有任何事先确定的等级结构。

 

缺点

  • 每个类必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对已经有的类不一定很容易,特别是当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

 

参考文献

  • 《Java与模式》 作者:阎宏,电子工业出版社
  • 《大话设计模式》 作者:程杰,清华大学出版社

 

 

 

 

Categories: 设计模式