[TOC]
模式定義
原型模式(Prototype Pattern):原型模式是提供一個(gè)原型接口,提供原型的克隆,創(chuàng)建新的對(duì)象,是一種對(duì)象創(chuàng)建型模式。
模式結(jié)構(gòu)
原型模式包括如下角色
- Prototype :抽象原型類
- ConcretePrototype:具體原型類
- Client:客戶類
原型模式類別
一個(gè)類包括另外一個(gè)成員變量,在使用原型模式進(jìn)行對(duì)象克隆時(shí),如果直接是通過super Cloneable接口的的clone方法,這種情況其實(shí)并不支持類中另外一些成員變量的克隆的,這種方法稱之為淺克隆,所以淺克隆和深克隆的本質(zhì)區(qū)別就是看其是否支持類中的成員變量的克隆。
綜上,原型模式可以淺克隆和深克隆兩種情況,其區(qū)別是是否支持類中的成員變量的克隆。
原型模式的淺克隆
原型模式在Java里的常用實(shí)現(xiàn)是通過類繼承 JDK提供的Cloneable接口,重寫 clone(),這種方法其實(shí)也可以稱之為原型模式的淺克隆
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方法符合:
- 類型相同:對(duì)于任何對(duì)象a,a.clone().getClass() = a.getClass()
- 內(nèi)存地址不同:也可以說對(duì)于任何對(duì)象a,a.clone()!=a,克隆對(duì)象和原對(duì)象不是同一個(gè)對(duì)象
- a對(duì)象的equals方法:對(duì)于任何對(duì)象a,a.clone().equals(a)
淺克隆的例子,例子來自《設(shè)計(jì)模式》一書的郵件復(fù)制
由于郵件對(duì)象包含的內(nèi)容較多(如發(fā)送者、接收者、標(biāo)題、內(nèi)容、日期、附件等),某系統(tǒng)中現(xiàn)需要提供一個(gè)郵件復(fù)制功能,對(duì)于已經(jīng)創(chuàng)建好的郵件對(duì)象,可以通過復(fù)制的方式創(chuàng)建一個(gè)新的郵件對(duì)象,如果需要改變某部分內(nèi)容,無須修改原始的郵件對(duì)象,只需要修改復(fù)制后得到的郵件對(duì)象即可。使用原型模式設(shè)計(jì)該系統(tǒng)。在本實(shí)例中使用淺克隆實(shí)現(xiàn)郵件復(fù)制,即復(fù)制郵件(Email)的同時(shí)不復(fù)制附件(Attachment)。
附件類:
public class Attachment
{
public void download()
{
System.out.println("下載附件");
}
}
郵件類,淺克?。?/p>
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());
}
}
編譯返回,第一個(gè)是false,第二個(gè)是true,由前面的理論可以知道,淺克隆對(duì)于成員變量是不支持克隆的,因?yàn)閷?duì)象地址還是一樣的
原型模式的深克隆
上面是淺克隆的實(shí)現(xiàn),對(duì)于原型模式深克隆的實(shí)現(xiàn)一般是提供類的序列化來實(shí)現(xiàn)
附件類,注意要implements Serializable
import java.io.*;
public class Attachment implements Serializable
{
public void download()
{
System.out.println("下載附件");
}
}
郵件類,同樣要實(shí)現(xiàn)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
{
//將對(duì)象寫入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);
//將對(duì)象從流中取出
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());
}
}
編譯返回,第一個(gè)是false,第二個(gè)是flase,由前面的理論可以知道,深克隆對(duì)于成員變量是支持克隆的,因?yàn)閷?duì)象地址是一樣的
原型管理器
原型管理器是原型模式的拓展
例子同樣來自《設(shè)計(jì)模式》一書
import java.util.*;
interface MyColor extends Cloneable
{
public Object clone();
public void display();
}
class Red implements MyColor
{
public Object clone()
{
Red r=null;
try
{
r=(Red)super.clone();
}
catch(CloneNotSupportedException e)
{
}
return r;
}
public void display()
{
System.out.println("This is Red!");
}
}
class Blue implements MyColor
{
public Object clone()
{
Blue b=null;
try
{
b=(Blue)super.clone();
}
catch(CloneNotSupportedException e)
{
}
return b;
}
public void display()
{
System.out.println("This is Blue!");
}
}
class PrototypeManager
{
private Hashtable ht=new Hashtable();
public PrototypeManager()
{
ht.put("red",new Red());
ht.put("blue",new Blue());
}
public void addColor(String key,MyColor obj)
{
ht.put(key,obj);
}
public MyColor getColor(String key)
{
return (MyColor)((MyColor)ht.get(key)).clone();
}
}
class Client
{
public static void main(String args[])
{
PrototypeManager pm=new PrototypeManager();
MyColor obj1=(MyColor)pm.getColor("red");
obj1.display();
MyColor obj2=(MyColor)pm.getColor("red");
obj2.display();
System.out.println(obj1==obj2);
}
}
模式應(yīng)用
原型模式適用的場(chǎng)景
保存對(duì)象的狀態(tài):對(duì)于要保存的狀態(tài)不是很占內(nèi)存的情況,可以適用原型模式和備忘錄模式保存對(duì)象狀態(tài),如果對(duì)象占用太多內(nèi)存,那就還是狀態(tài)模式比較好
創(chuàng)建新對(duì)象成本很大的情況:比如創(chuàng)建一個(gè)對(duì)象是需要查詢很慢的SQL才能給對(duì)象賦值,這種情況就和適合用原型模式克隆對(duì)象,減少對(duì)象創(chuàng)建和查詢
原型模式應(yīng)用的場(chǎng)景
- 對(duì)于很多軟件的復(fù)制和粘貼實(shí)現(xiàn)其實(shí)也是原型模式的應(yīng)用
- Spring框架提供BeanUtils.copyProperties方法也是原型模式的應(yīng)用