在軟體系統中,有時候需要多次創建某一型別物件,為了簡化創建程序,可以只創建一個物件,然后再通過克隆的方法復制出多個相同的物件,這就是原型模式的設計思想,
模式定義
原型模式是一種物件創建模式,用原型實體指定創建物件的種類,并且通過復制這些原型創建新的物件,
模式結構

-
Prototype(抽象原型類)
抽象原型類是定義具有克隆自己方法的介面,是所有具體原型類的公共父類,可以是抽象類也可以是介面,
-
ConcretePrototype(具體原型類)
具體原型類實作具體克隆方法,在克隆方法中回傳自己的一個克隆物件
-
Client(客戶類)
讓一個原型克隆自身,從而創建一個新的物件,在客戶類中只需直接實體化或通過工廠方法等方式創建一個物件,再通過呼叫該物件的克隆方法復制多個相同的物件,
Java對原型模式的支持
Java語言中的原型模式實作很簡單,原型模式結構中定義了一個抽象原型類,所有的Java類都繼承自java.lang.Object,而Object類提供一個clone方法,可以將一個Java物件復制一份,因此在Java中可以直接使用Object提供的clone()方法來實作物件的克隆,
需要注意的是能實作克隆的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語言原型模式的實作
Java語言提供的clone()方法將物件復制一份并回傳給呼叫者,一般而言,clone()方法滿足:
- 對任何物件x,都有clone() != x,即克隆物件與原物件不是同一個物件,
- 對任何物件x,都有x.clone().getClass() == x.getClass(),即克隆物件與原物件的型別一樣,
- 如果物件的equals()方法定義恰當,那么x.clone().equals(x)應該成立,
為了獲取物件的一份拷貝,可以利用Object類的clone()方法,具體步驟如下
- 在派生類中覆寫基類的clone()方法,并宣告為public,
- 在派生類的clone()方法中,呼叫super.clone(),
- 在派生類中實作Cloneable介面
通過覆寫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()); } } -
結果分析
編譯并運行客戶端測驗類,輸出結果如下:

通過結果可以看出,復制得到的物件與原型物件的參考不一致,原型物件與克隆物件對成員物件的參考不相同,說明其成員物件也復制了一份,
-
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/83462.html
標籤:其他
上一篇:任何人都需要知道的「世界時間系統」構成原理,尤其開發人員
下一篇:最小的k個數
