設計模式之原型模式(建立型)
[TOC]
模式定義
原型模式(Prototype Pattern):原型模式是提供一個原型介面,提供原型的克隆,建立新的物件,是一種物件建立型模式。
模式結構
原型模式包括如下角色
- Prototype :抽象原型類
- ConcretePrototype:具體原型類
- Client:客戶類
原型模式類別
一個類包括另外一個成員變數,在使用原型模式進行物件克隆時,如果直接是通過super Cloneable介面的的clone方法,這種情況其實並不支援類中另外一些成員變數的克隆的,這種方法稱之為淺克隆,所以淺克隆和深克隆的本質區別就是看其是否支援類中的成員變數的克隆。
綜上,原型模式可以淺克隆和深克隆兩種情況,其區別是是否支援類中的成員變數的克隆。
原型模式的淺克隆
原型模式在Java裡的常用實現是通過類繼承 JDK提供的Cloneable介面,重寫 clone(),這種方法其實也可以稱之為原型模式的淺克隆
public class A implements Cloneable { public Object clone() { A clone=null; try { clone=(A)super.clone(); } catch(CloneNotSupportedException e) { System.out.println("Clone failure!"); } return clone; } }
一般來說,clone方法符合:
- 型別相同:對於任何物件a,a.clone().getClass() = a.getClass()
- 記憶體地址不同:也可以說對於任何物件a,a.clone()!=a,克隆物件和原物件不是同一個物件
- a物件的equals方法:對於任何物件a,a.clone().equals(a)
淺克隆的例子,例子來自《設計模式》 一書的郵件複製
由於郵件物件包含的內容較多(如傳送者、接收者、標題、內容、日期、附件等),某系統中現需要提供一個郵件複製功能,對於已經建立好的郵件物件,可以通過複製的方式建立一個新的郵件物件,如果需要改變某部分內容,無須修改原始的郵件物件,只需要修改複製後得到的郵件物件即可。使用原型模式設計該系統。在本例項中使用淺克隆實現郵件複製,即複製郵件(Email)的同時不復制附件(Attachment)。
附件類:
public class Attachment { public void download() { System.out.println("下載附件"); } }
郵件類,淺克隆:
public class Email implements Cloneable { private Attachment attachment=null; public Email() { this.attachment=new Attachment(); } public Object clone() { Email clone=null; try { clone=(Email)super.clone(); } catch(CloneNotSupportedException e) { System.out.println("Clone failure!"); } return clone; } public Attachment getAttachment() { return this.attachment; } public void display() { System.out.println("檢視郵件"); } }
客戶端類:
public class Client { public static void main(String a[]) { Email email,copyEmail; email=new Email(); copyEmail=(Email)email.clone(); System.out.println("email==copyEmail?"); System.out.println(email==copyEmail); System.out.println("email.getAttachment==copyEmail.getAttachment?"); System.out.println(email.getAttachment()==copyEmail.getAttachment()); } }
編譯返回,第一個是false,第二個是true,由前面的理論可以知道,淺克隆對於成員變數是不支援克隆的,因為物件地址還是一樣的
原型模式的深克隆
上面是淺克隆的實現,對於原型模式深克隆的實現一般是提供類的序列化來實現
附件類,注意要implements Serializable
import java.io.*; public class Attachment implements Serializable { public void download() { System.out.println("下載附件"); } }
郵件類,同樣要實現Serializable介面
import java.io.*; public class Email implements Serializable { private Attachment attachment=null; public Email() { this.attachment=new Attachment(); } public Object deepClone() throws IOException, ClassNotFoundException, OptionalDataException { //將物件寫入流中 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()); } public Attachment getAttachment() { return this.attachment; } public void display() { System.out.println("檢視郵件"); } }
客戶端類:
public class Client { public static void main(String a[]) { Email email,copyEmail=null; email=new Email(); try{ copyEmail=(Email)email.deepClone(); } catch(Exception e) { e.printStackTrace(); } System.out.println("email==copyEmail?"); System.out.println(email==copyEmail); System.out.println("email.getAttachment==copyEmail.getAttachment?"); System.out.println(email.getAttachment()==copyEmail.getAttachment()); } }
編譯返回,第一個是false,第二個是flase,由前面的理論可以知道,深克隆對於成員變數是支援克隆的,因為物件地址是一樣的
模式應用
原型模式適用的場景
-
儲存物件的狀態:對於要儲存的狀態不是很佔記憶體的情況,可以適用原型模式和備忘錄模式儲存物件狀態,如果物件佔用太多記憶體,那就還是狀態模式比較好
-
建立新物件成本很大的情況:比如建立一個物件是需要查詢很慢的SQL才能給物件賦值,這種情況就和適合用原型模式克隆物件,減少物件建立和查詢
原型模式應用的場景
- 對於很多軟體的複製和貼上實現其實也是原型模式的應用
- Spring框架提供BeanUtils.copyProperties方法也是原型模式的應用