概述
原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。
原型模式主要使用场景:
1.类初始化需要消耗非常多的资源。
2.通过new产生一个对象需要非常繁琐的数据准备或者访问权限。
原型模式的两种形式
原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。
原型模式的两种表现形式:1.简单形式,2.登记形式。
简单形式的原型模式:
UML类图如下:
上述的UML图中,存在三个角色:
1。Client角色:客户类提出创建对象的请求。
2。抽象原型(Prototype)角色:抽象的角色,由接口或者抽象类实现,用来定义具体原型类所需的接口。
3。具体原型(Concrete Prototype)角色:被复制的对象,此角色需要实现抽象的原型角色所要求的接口。
代码示例:
interface Prototype {
public Object clonePrototype();
}
class ConcretePrototype implements Prototype{
public Prototype clonePrototype() {
Prototype prototype = new ConcretePrototype();
return prototype;
}
}
class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype = prototype;
}
public void operation(){
Prototype pro = (Prototype) prototype.clonePrototype();
}
}
public class MainTest{
public static void main(String args[]){
Prototype prototype = new ConcretePrototype();
Client client = new Client(prototype);
client.operation();
}
}
登记形式的原型模式:
UML图如下所示:
上述UML中较之简单实行多一个角色,原型管理器角色(PrototypeManager),该角色主要是用来创建具体原型类的对象,并记录每一个被创建的对象。
代码示例:
import java.util.HashMap;
import java.util.Map;
interface Prototype {
public Object clonePrototype();
}
class ConcretePrototype implements Prototype{
public Prototype clonePrototype() {
Prototype prototype = new ConcretePrototype();
return prototype;
}
}
class ProtoTypeManager{
private static Map<String, Prototype> map = new HashMap<String, Prototype>();
private ProtoTypeManager(){};
public synchronized static void setPrototype(String prototypeId,Prototype prototype){
map.put(prototypeId, prototype);
}
public synchronized static void removePrototype(String prototypeId){
map.remove(prototypeId);
}
public synchronized static Prototype getPrototype(String prototypeId) throws Exception{
Prototype prototype = map.get(prototypeId);
if(prototype == null){
throw new Exception("您希望获取的原型还没有注册或已被销毁");
}
return prototype;
}
}
public class Client{
public static void main(String args[]) throws Exception{
Prototype prototype = new ConcretePrototype();
ProtoTypeManager.setPrototype("p1", prototype);
Prototype p2 = (Prototype) ProtoTypeManager.getPrototype("p1").clonePrototype();
}
}
两种形式的比较:
简单形式和登记形式的原型模式各有优缺点。
如果需要创建的原型模式对象数目较少而且比较固定的话,可以采用第一种形式。在这种情况下,原型对象的引用可以由客户端自己保存。
如果要创建的原型对象数目不固定的话,可以采取第二种形式,在这种情况下,客户端不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象。如果有,可以直接从管理员类中取得这个对象的引用,如果没有,客户端需要自行复制此原型对象。
原型模式使用Java的“克隆”功能
Java的所有类都是从Java.lang.Object类继承而来的,Object类中有一个clone方法,用来返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用。因此,Prototype类需要将clone方法的作用域修改为public类型。
实现Cloneable接口,它在运行时通知虚拟机可以安全的实现了此接口的类上使用clone方法。只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException。
代码示例:
class Prototype implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
Prototype prototype = null;
try {
prototype = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
// TODO: handle exception
e.printStackTrace();
}
return prototype;
}
}
class ConcretePrototype extends Prototype{
int i = 2;
public void show(){
System.out.println("原型模式实现类"+i);
}
}
public class Client{
public static void main(String args[]){
ConcretePrototype concretePrototype = new ConcretePrototype();
for (int i = 0; i < 5; i++) {
try {
ConcretePrototype con = (ConcretePrototype) concretePrototype.clone();
con.show();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
使用这个方式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能差别非常明显。
通过调用Object类的clone方法来完成对象的复制,它不会调用到类的构造方法,而且访问权限对clone无效。
单例模式中,只需要将构造方法的访问权限设置为private型,就可以实现单例。所以单例模式与原型模式是冲突的,使用时需要注意。
关于深拷贝和浅拷贝。Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,容器对象,引用对象都不会拷贝,这就是浅拷贝,如果要实现深拷贝,必须将原型模式中的数组,引用对象等另行拷贝。