前言
類似于《西游記》中的孫悟空拔出猴毛社露,根據(jù)自己的樣子變出很多猴子來猜嘱⊙镀或者是《火影忍者》中鳴人使用影分身變出很多個鳴人來纫普。設(shè)計模式中的原型模式,也是根據(jù)原型(如同孫悟空好渠,鳴人本人就是原型)創(chuàng)建出新的對象昨稼。
是什么
原型模式(prototype pattern)是一種創(chuàng)建型模式,使用原型實例指定創(chuàng)建對象的種類拳锚,并且通過拷貝這些原型創(chuàng)建新的對象假栓。
為什么
原型模式多用于創(chuàng)建復(fù)雜的或構(gòu)造耗時的實例,這樣就能高效地創(chuàng)建實例對象霍掺,減輕創(chuàng)建對象的成本匾荆。
怎么做
在Java中,所有的類都繼承自java.lang.Object
類杆烁,而該類提供了clone()
方法牙丽,這個本地方法可以將Java對象復(fù)制一份。但是需要注意的是兔魂,必須要實現(xiàn)標識接口Cloneable
來標識這個類可以被復(fù)制烤芦。
下面通過一個例子來實現(xiàn)原型模式,這是一個實現(xiàn)了Cloneable接口的類析校,并重載了Object類的clone方法拍棕。
/**
* 實現(xiàn)Cloneable接口表示該類可以被復(fù)制
*/
public class Sheep implements Cloneable{
private String name;
private Date birthDate;
public Sheep(String name,Date date) {
this.name = name;
this.birthDate = date;
}
public Date getBirthDate() {
return birthDate;
}
public String getName() {
return name;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public void setName(String name) {
this.name = name;
}
/**
* 重載Object類的clone方法
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Object clone() throws CloneNotSupportedException {
// 淺復(fù)制
return super.clone();
}
@Override
public String toString() {
return "Sheep<"+hashCode()+">'s name="+name+" birthdate:"
+birthDate.toString()+" name hashcode:"+name.hashCode()+" birthdate hashcode:"+birthDate.hashCode();
}
@Override
public boolean equals(Object obj) {
Sheep another = (Sheep) obj;
return this.name.equals(another.name)&&this.birthDate.compareTo(another.birthDate)==0;
}
}
現(xiàn)在我們來測試一下上面那個類對象的復(fù)制,
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Date birthDate = new Date(1341314415234L);
String name = "duoli";
Sheep duoli = new Sheep(name,birthDate);
Sheep cloneSheep = (Sheep) duoli.clone();
System.out.println(duoli.toString());
System.out.println(cloneSheep.toString());
birthDate.setTime(2124124124L);
System.out.println(duoli.toString());
System.out.println(cloneSheep.toString());
}
}
從上面運行結(jié)果截圖可以知道勺良,原型對象和克隆對象的值是一樣的绰播,但是修改原型對象的屬性之后,克隆對象的相應(yīng)屬性也被修改了尚困。這是由于淺復(fù)制導(dǎo)致的蠢箩。也就是說克隆對象只復(fù)制了原型對象的地址。當該地址對應(yīng)的原型對象的值發(fā)生變化時事甜,有著相同地址的克隆對象的屬性也會發(fā)生變化谬泌。
這個問題可以通過深復(fù)制來解決,在深復(fù)制中逻谦,除了對象本身被復(fù)制外掌实,對象所有的屬性也會被復(fù)制。
對于Sheep
類邦马,我們可以進行如下的修改:
...
@Override
protected Object clone() throws CloneNotSupportedException {
//Deep Clone
Sheep sheep = (Sheep) super.clone();
sheep.birthDate = (Date) birthDate.clone();
sheep.name = name;
return sheep;
}
...
雖然贱鼻,這種方式看起來比較簡單宴卖,這是由于我們直接使用了引用類型Date
類已經(jīng)實現(xiàn)好了的clone
方法。
對于我們自定義的類邻悬,往往我們要去實現(xiàn)Cloneable
接口症昏,并重寫Object
類的clone()
方法。
我們還可以通過序列化的方式(Serialization)來實現(xiàn)父丰。通過IO流操作把對象寫入流中肝谭,流中的對象就是原有對象的拷貝,不僅復(fù)制了對象本身蛾扇,而且可以復(fù)制其引用的成員屬性攘烛。所以,將對象寫入流中镀首,然后從流中讀出來坟漱,就能實現(xiàn)深復(fù)制了。
下面通過將clone方法中修改為序列化操作來實現(xiàn)深復(fù)制:
@Override
protected Object clone() throws CloneNotSupportedException {
//Deep Clone
// Sheep sheep = (Sheep) super.clone();
// sheep.birthDate = (Date) birthDate.clone();
// sheep.a = (A) a.clone();
// sheep.name = name;
// return sheep;
try {
return deepClone();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 使用IO技術(shù)實現(xiàn)深復(fù)制
* @return
*/
public Sheep deepClone() throws IOException, ClassNotFoundException {
//將對象寫入IO流中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
//將對象從IO流中取出來
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Sheep clone = (Sheep) ois.readObject();
baos.close();
oos.close();
bais.close();
ois.close();
return clone;
}