设计模式之原型模式
1 概述
原型模式(Prototype model)被用在频繁调用且极其相似的对象上,在已指定对象的基础上,然后通过拷贝这些原型对象创建新的对象。
Prototype类需要具备以下两个条件:
(1)实现Cloneable接口。在Java语言有一个Cloneable接口,它的目的是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
(2)重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
2 示例
下面创建一个简单的原形类,它实现了Cloneable接口,虽然这个接口中啥方法都木有。
1 package org.scott.prototype; 2 /** 3 * @author Scott 4 * @version 2013-11-21 5 * @description 6 */ 7 public class ProtoType implements Cloneable{ 8 public Object clone() throws CloneNotSupportedException { 9 ProtoType proto = (ProtoType) super.clone(); 10 return proto; 11 } 12 }
调用clone方法就能创建出一个新的“副本”出来。
重点是super.clone()方法,这是Object类自带的。这个方法有点意思。它的目的是创建并返回当前对象的一个副本,所谓的“副本”,是和当前对象的类相关。这个方法的实现有点要求,Object类自带的这个方法实现是Native方式。对于普通的创建副本的方法而言,对任意一个对象X而言,
x.clone() != x
可能为true,而
x.clone().getClass() == x.getClass()
也可能为true,但都不是绝对要满足的要求。
x.clone().equals(x)
也一样可能为true,只是“可能”而已。
如果一个类及其所有的超类(Object除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。
一般来说,调用super.clone()方法都是浅复制,也就是说引用的变量还是指向原来的Object,而基本数据类型都会重建一个对应的副本出来。相对的,自然还有深复制,不管是不是引用类型,都将创建一个全新的副本出来,复制的很彻底。深度复制的实现就要借用二进制流进行重建对象。
1 package org.scott.prototype; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.IOException; 6 import java.io.ObjectInputStream; 7 import java.io.ObjectOutputStream; 8 import java.io.Serializable; 9 10 /** 11 * @author Scott 12 * @version 2013-11-21 13 * @description 14 */ 15 public class ProtoType2 implements Cloneable, Serializable{ 16 private static final long serialVersionUID = 1L; 17 18 private String string; 19 private SerializableObject obj; 20 21 public Object shallowClone() throws CloneNotSupportedException { 22 ProtoType2 proto = (ProtoType2) super.clone(); 23 return proto; 24 } 25 26 public Object deepClone() throws IOException, ClassNotFoundException { 27 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 28 ObjectOutputStream oos = new ObjectOutputStream(bos); 29 oos.writeObject(this); 30 31 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 32 ObjectInputStream ois = new ObjectInputStream(bis); 33 return ois.readObject(); 34 } 35 36 public String getString() { 37 return string; 38 } 39 40 public void setString(String string) { 41 this.string = string; 42 } 43 44 public SerializableObject getObj() { 45 return obj; 46 } 47 48 public void setObj(SerializableObject obj) { 49 this.obj = obj; 50 } 51 52 public static void main(String args[]) throws CloneNotSupportedException, IOException, ClassNotFoundException{ 53 ProtoType2 proto = new ProtoType2(); 54 55 System.out.println("shallow copy, equal:" + proto.equals(proto.shallowClone())); 56 System.out.println("shallow copy, ==:" + (proto == proto.shallowClone())); 57 System.out.println("shallow copy, getClass:" + (proto.getClass() == proto.shallowClone().getClass())); 58 59 System.out.println("----------------------------------------------------"); 60 61 System.out.println("deep copy, equal:" + proto.equals(proto.deepClone())); 62 System.out.println("deep copy, ==:" + (proto == proto.deepClone())); 63 System.out.println("deep copy, getClass:" + (proto.getClass() == proto.deepClone().getClass())); 64 } 65 } 66 67 class SerializableObject implements Serializable { 68 private static final long serialVersionUID = 1L; 69 }
运行结果:
shallow copy, equal:false shallow copy, ==:false shallow copy, getClass:true ---------------------------------------------------- deep copy, equal:false deep copy, ==:false deep copy, getClass:true
当然,如果原型类中的成员变量类型不同,上面这个结果自然也不同。
上个UML设计图: