java语言的构建模型直接支持原始模型模式,所有的javabean都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个javaBean对象复制一份。但是这个javaBean必须实现一个标示接口Cloneable,表面这个javaBean支持复制。如果一个对象没有实现这个接口而调用clone()方法,java编译器会抛出CloneNotSupportedException异常。
原始模型模式的结构
原始模型模式有两种表现形式:第一种是简单形式,第二种是等级形式。这两种表现形式仅仅是原始模型模式中的不同实现,但是由于它们的区别影响了模式结构的细节。 简单形式的原始模型模式 简单形式的原始模型模式类图如下:![](http://static.oschina.net/uploads/space/2013/0322/132812_rqb6_123079.jpg)
- 客户(Client)角色: 客户类提出创建对象的请求。
- 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个java接口或者java抽象类实现,此角色给出所有的具体原型类所需要的接口。
- 具体原型(Concrete Prototype)角色:被复制的对象,此角色需要实现抽象原型角色所要求的接口。
登记形式的原始模型模式 登记形式的原始模型模式的类图如下:
![](http://static.oschina.net/uploads/space/2013/0322/143509_Fkst_123079.jpg)
- 客户端(Client)角色:客户端类向管理员提出创建对象的请求。
- 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个接口或者抽象类实现。此角色给出所有的具有原型类所需的接口。
- 具体原型(Concrete Prototype)角色:被复制的对象,需要实现抽象原型角色所要求的接口。
- 原型管理器(Prototype Manager)角色: 创建具体原型类的对象,并记录每一个被创建的对象。
package com.model.builder.propotypeclient;public interface Prototype extends Cloneable{ public Object clone(); }2.具体原型角色 ConcretePrototype.java
package com.model.builder.propotypeclient;public class ConcretePrototype implements Prototype { public synchronized Object clone(){ Prototype temp=null; try{ temp = (Prototype)super.clone(); return temp; }catch(Exception e){ System.out.println("Clone failed"); }finally{ return temp; } }}3.原型管理器角色 PrototypeManager.java
package com.model.builder.propotypeclient;import java.util.ArrayList;import java.util.List;public class PrototypeManager { private List4.客户端角色 Client.java
package com.model.builder.propotypeclient;public class Client { private PrototypeManager pm ; private Prototype po ; public void registerPrototype(){ po = new ConcretePrototype(); Prototype copytype = (Prototype)po.clone(); pm.add(copytype); } }
两种形式的比较 简单形式和登记形式的原始模型模式各有其长处和短处。 如果需要创建的原型对象数目较少,而且比较固定,可以采用简单形式的原始模型模式,这种情况下,原型对象的引用可以由客户端自己保存。
如果要创建的原型对象的数目不固定,可以采用登记形式的原始模型模式,在这种情况下,客户端并不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象,如果有,则直接从管理员类中取得这个对象的引用,如果没有,客户端就需自行复制此原型对象。
模式的实现:深复制和浅复制
浅复制 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。换而言之,浅复制仅复制所考虑的对象,而不复制它所引用的对象。 深复制 被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被被复制过的新对象,而不在是原有的那些被引用的对象,换而言之,深复制把要复制的对象所引用的对象都复制了一遍,而这种对被引用的对象的复制叫做间接复制。 深复制要深入到多少层,是一个不易确定的问题。在决定以深复制的方式复制一个对象的时候,必须决定对间接复制的对象是采取浅复制还是继续采取深复制。因此,在采取深复制时,需要决定多深才算深。此外,在深复制过程中,很可能会出现循环引用的问题,必须小心处理。 利用串行化来进行深复制 把对象写入流里的过程是串行化过程,但是在java程序猿圈子里又非常形象的称为“冷冻”或者“腌咸菜(picking)”过程,而把对象从流中读出来的并行化的过程叫做“解冻”或者“回鲜(depicking)”过程。应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM中,因此“腌成咸菜”的只是对象的一个拷贝,还可以回鲜。 在java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象写入到一个流里,再从流中读回来,就可以重建对象。源码如下: DeepClone.javapackage com.model.builder.propotypeclient;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class DeepClone { public Object deepClone() throws Exception{ //将对象写入到流里 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //从流里读回阿里 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return oi.readObject(); }}浅复制显然比深复制更加容易实现,因为java语言的所有类都会继承一个Clone()方法,而这个方法所做的正是浅复制。 有一些对象,比如线程(thread)对象和 Socket对象,是不能简单复制或共享的。不管是使用深复制或者是浅复制,只要涉及到这样的间接对象,都必须把间接对象设置成transient,而不予复制,或者由程序自行创建出相当的同种对象了,权且当做复制件使用。 原始模型模式的优点和缺点 抽象工厂模式有许多与原始原型模式和建造模式相同的效果,包括客户端不知道具体产品类,只知道抽象产品类,客户端不需要知道这么多具体产品名称,如果有新的产品类加入,客户端不需要修改就可直接使用。 原型模式有其特有的优点: (1)原始原型模式运行动态滴增加或减少产品类。由于创建产品类实例的方法是产品内部具有的,因此增加新产品对整个结构没有影响。 (2)原始模型模式提供简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而原始模型模式不需要这样。对于java设计师来说,原始模型模式有其特有的方便之处,因为java语言天生就将原始模型模式设计到了语言模型里面,善于利用原始模型模式和java语言的特点,可以事半功倍。 (3)具有给一个应用软件动态加载新功能的能力,例如一个分析Web服务器的记录文件的应用软件,针对每一种记录文件格式,都可以由一个相应的“格式类”负责。如果出现了应用软件所不支持的新的Web服务器,只需要提供一个格式类的克隆,并在客户端登记即可,不必给每个软件的用户提供一个全新的软件包。 (4)产品类不需要非得有任何事先确定的产品等级结构,因为原始原型模式适用于任何的等级结构。 原始原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已有的类却不一定很容易,特别是一个类的引用不支持串行化的间接对象,或者引用含有循环结构的时候。
原始模型模式与其他模式的关系
原始模型模式与合成(Composite)模式的关系 原始模型模式经常与合成模式一起使用,因为原先对象经常是合成对象。 原始模型模式与抽象工厂(Abstract Factory)模式的关系 如果系统不需要动态的改变原型对象,抽象工厂模式可以替代原始原型模式。 原始模型模式与 门面(Facade)模式的关系 原始模型模式的客户端通常可以将系统的其他对象与参与原始模型模式的对象分隔开,起到一个门面对象的作用。 原始模型模式与装饰(Decorator)模式的关系 原始模型模式常常与装饰模式一起使用,java的io库广泛使用了装饰模式,串行化的过程也是一个使用装饰模式的过程。