在軟件系統中,有時候須要屢次建立某一類型對象,爲了簡化建立過程,能夠只建立一個對象,而後再經過克隆的方法複製出多個相同的對象,這就是原型模式的設計思想。java
原型模式是一種對象建立模式,用原型實例指定建立對象的種類,而且經過複製這些原型建立新的對象。ide
Prototype(抽象原型類)測試
抽象原型類是定義具備克隆本身方法的接口,是全部具體原型類的公共父類,能夠是抽象類也能夠是接口。this
ConcretePrototype(具體原型類)prototype
具體原型類實現具體克隆方法,在克隆方法中返回本身的一個克隆對象設計
Client(客戶類)code
讓一個原型克隆自身,從而建立一個新的對象,在客戶類中只需直接實例化或經過工廠方法等方式建立一個對象,再經過調用該對象的克隆方法複製多個相同的對象。對象
Java語言中的原型模式實現很簡單,原型模式結構中定義了一個抽象原型類,全部的Java類都繼承自java.lang.Object,而Object類提供一個clone方法,能夠將一個Java對象複製一份。所以在Java中能夠直接使用Object提供的clone()方法來實現對象的克隆。blog
須要注意的是能實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持複製。若是一個類沒有實現這個接口但調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。繼承
public class PrototypeDemo implements Cloneable { @Override protected Object clone() { Object o = null; try { o = super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Not support cloneable"); } return o; } }
public class Client { PrototypeDemo prototypeDemo = new PrototypeDemo(); PrototypeDemo prototypeDemo2 = (PrototypeDemo) prototypeDemo.clone(); }
一般狀況下,一個類包含一些成員對象,在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式可分爲兩種形式:深克隆和淺克隆。
淺克隆
被複制對象的全部普通成員變量都具備與原來的對象相同的值,而全部對其餘對象的引用仍然指向原來對象。
obj1是原型對象,obj2爲複製後對象,containedObj1和containedObj2爲成員對象。
深克隆
引用其餘對象的變量將指向被複制過的新對象,而不是原有被引用的對象。
Java語言提供的clone()方法將對象複製一份並返回給調用者,通常而言,clone()方法知足:
爲了獲取對象的一份拷貝,能夠利用Object類的clone()方法,具體步驟以下
經過覆蓋Object類的clone()方法實現淺克隆,若是須要實現深克隆,能夠經過序列化等方式來實現。
因爲郵件對象包含的內容較多(如發送者、標題、內容、日期、附件等),某系統現需提供一個郵件複製功能,對於已經建立好的郵件對象,能夠經過複製的方式建立一個新的郵件對象,若是須要改變某部份內容,無須修改原始的郵件對象,只須要修改複製後獲得的郵件對象便可。本例使用淺克隆實現郵件複製,即複製郵件(E-mail)的同時不復制附件。
抽象原型類Object
Object做爲抽象原型類,提供了克隆方法clone(),用於建立一個原型對象,其clone()方法由JVM完成具體實現,用戶在使用時無須關心。
public class Object { protected native Object clone() throws CloneNotSupportedException; }
具體原型類Email(郵件類)
public class Email implements Cloneable { private Attachment attachment = null; public Email() { } public Email(Attachment attachment) { this.attachment = attachment; } public Attachment getAttachment() { return attachment; } @Override public Object clone() { Email clone = null; try { clone = (Email) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
附件類Attachment
public class Attachment { public void download() { System.out.println("下載附件"); } }
客戶端測試類Client
public class Client { public static void main(String[] args) { Email email = new Email(new Attachment()); Email copyEmail = (Email) email.clone(); System.out.println(email == copyEmail); System.out.println(email.getAttachment() == copyEmail.getAttachment()); } }
結果分析
編譯並運行客戶端測試類,輸出結果以下:
經過結果能夠看出,複製獲得的對象與原型對象的引用不一致,但兩個對象的成員對象是同一個,說明雖然對象自己複製了一份,但其成員對象在內存中沒有複製,原型對象與克隆對象都維持了對相同成員對象的引用。
用深克隆實現郵件複製
具體原型類Email(郵件類)
Email做爲具體類,因爲實現的是深克隆,無須使用Object的clone()方法;經過序列化的方法實現深克隆,因爲要將Email類型對象寫入流中,所以Email類需實現Serializable接口。
public class Email implements Serializable { private Attachment attachment = null; public Email() { this.attachment = new Attachment(); } public Attachment getAttachment() { return attachment; } public Object deepclone() throws IOException, ClassNotFoundException { //將對象寫入流中 ByteArrayOutputStream bao = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bao); oos.writeObject(this); //將對象從流中取出 ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (ois.readObject()); } }
附件類Attachment
Attachment類型對象也將被寫入流中,所以也需實現Serializable接口。
public class Attachment implements Serializable { public void download() { System.out.println("下載附件"); } }
客戶端測試類Client
public class Client { public static void main(String[] args) { Email email, copyEmail = null; email = new Email(); try { copyEmail = (Email) email.deepclone(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(email == copyEmail); System.out.println(email.getAttachment() == copyEmail.getAttachment()); } }
結果分析
編譯並運行客戶端測試類,輸出結果以下:
經過結果能夠看出,複製獲得的對象與原型對象的引用不一致,原型對象與克隆對象對成員對象的引用不相同,說明其成員對象也複製了一份。