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設計圖: