描述
????原型模式屬于對象的創(chuàng)建模式。通過給出一個原型對象來指明所有創(chuàng)建的對象的類型蜘欲,然后用復制這個原型對象的辦法創(chuàng)建出更多同類型的對象。
簡介
????原型模式要求對象實現(xiàn)一個可以“克隆”自身的接口晌柬,這樣就可以通過復制一個實例對象本身來創(chuàng)建一個新的實例姥份。這樣一來,通過原型實例創(chuàng)建新的對象年碘,就不再需要關(guān)心這個實例本身的類型澈歉,只要實現(xiàn)了克隆自身的方法,就可以通過這個方法來獲取新的對象屿衅,而無須再去通過new來創(chuàng)建埃难。
角色
- 客戶(Client)角色:客戶類提出創(chuàng)建對象的請求。
- 抽象原型(Prototype)角色:這是一個抽象角色,通常由一個Java接口或Java抽象類實現(xiàn)凯砍。此角色給出所有的具體原型類所需的接口箱硕。
- 具體原型(Concrete Prototype)角色:被復制的對象。此角色需要實現(xiàn)抽象的原型角色所要求的接口悟衩。
優(yōu)缺點
優(yōu)點
- 當創(chuàng)建對象的實例較為復雜的時候剧罩,使用原型模式可以簡化對象的創(chuàng)建過程,通過復制一個已有的實例可以提高實例的創(chuàng)建效率座泳。
- 擴展性較好惠昔,由于在原型模式中提供了抽象原型類,在客戶端可以針對抽象原型類進行編程挑势,而將具體原型類寫在配置文件中镇防,增加或減少產(chǎn)品類對原有系統(tǒng)都沒有任何影響。
- 原型模式提供了簡化的創(chuàng)建結(jié)構(gòu)潮饱,工廠方法模式常常需要有一個與產(chǎn)品類等級結(jié)構(gòu)相同的工廠等級結(jié)構(gòu)来氧,而原型模式就不需要這樣,原型模式中產(chǎn)品的復制是通過封裝在原型類中的克隆方法實現(xiàn)的香拉,無須專門的工廠類來創(chuàng)建產(chǎn)品啦扬。
- 可以使用深克隆方式保存對象的狀態(tài),使用原型模式將對象復制一份并將其狀態(tài)保存起來凫碌,以便在需要的時候使用(例如恢復到歷史某一狀態(tài))扑毡,可輔助實現(xiàn)撤銷操作。
缺點
- 原型模式的每一個類都必須配備一個克隆方法盛险。配備克隆方法需要對類的功能進行通盤考慮瞄摊,這對于全新的類來說不是很難,而對于已經(jīng)有的類不一定很容易苦掘,且修改源代碼换帜,違背了“開閉原則”。
- 在實現(xiàn)深克隆時需要編寫較為復雜的代碼鹤啡,而且當對象之間存在多重的嵌套引用時膜赃,為了實現(xiàn)深克隆,每一層對象對應的類都必須支持深克隆揉忘,實現(xiàn)起來可能會比較麻煩。
使用場景
- 創(chuàng)建新對象成本較大(如初始化需要占用較長的時間端铛,占用太多的CPU資源或網(wǎng)絡資源)泣矛,新的對象可以通過原型模式對已有對象進行復制來獲得,如果是相似對象禾蚕,則可以對其成員變量稍作修改您朽。
- 如果系統(tǒng)要保存對象的狀態(tài),而對象的狀態(tài)變化很小,或者對象本身占用內(nèi)存較少時哗总,可以使用原型模式配合備忘錄模式來實現(xiàn)几颜。
克隆
克隆滿足的條件
- 對任何的對象x,都有:x.clone()!=x讯屈。換言之蛋哭,克隆對象與原對象不是同一個對象。(必須)
- 對任何的對象x涮母,都有:x.clone().getClass() == x.getClass()谆趾,換言之,克隆對象與原對象的類型一樣叛本。(必須)
- 如果對象x的equals()方法定義其恰當?shù)脑捇ε睿敲磝.clone().equals(x)應當成立的。(可選)
淺克隆和深克隆
- 淺克吕春颉:只負責克隆按值傳遞的數(shù)據(jù)(比如基本數(shù)據(jù)類型跷叉、String類型),而不復制它所引用的對象营搅,換言之云挟,所有的對其他對象的引用都仍然指向原來的對象。
- 深克戮绶馈:除了淺度克隆要克隆的值外植锉,還負責克隆引用類型的數(shù)據(jù)。那些引用其他對象的變量將指向被復制過的新對象峭拘,而不再是原有的那些被引用的對象俊庇。換言之,深度克隆把要復制的對象所引用的對象都復制了一遍鸡挠,而這種對被引用到的對象的復制叫做間接復制辉饱。
示例
????為了更好的理解原型模式以及淺克隆與深克隆的區(qū)別,下面我們用西游記中孫悟空的分身術(shù)來做講解拣展。
淺克隆
public class GoldRingedStaff {
private float height = 100.0f;
private float diameter = 10.0f;
/**
* 增長行為彭沼,每次調(diào)用長度和半徑增加一倍
*/
public void grow() {
this.diameter *= 2;
this.height *= 2;
}
/**
* 縮小行為,每次調(diào)用長度和半徑減少一半
*/
public void shrink() {
this.diameter /= 2;
this.height /= 2;
}
}
public class Monkey implements Cloneable {
private int height;
private int weight;
private Date birthDate;
private GoldRingedStaff staff;
@Override
protected Object clone() throws CloneNotSupportedException {
Monkey temp = null;
try {
temp = (Monkey) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
return temp;
}
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public GoldRingedStaff getStaff() {
return staff;
}
public void setStaff(GoldRingedStaff staff) {
this.staff = staff;
}
@Override
public String toString() {
return "Monkey{" +
"height=" + height +
", weight=" + weight +
", birthDate=" + birthDate +
", staff=" + staff +
'}';
}
}
public class Client {
public static void main(String[] args) {
Monkey monkey = new Monkey();
monkey.setHeight(170);
monkey.setWeight(100);
monkey.setBirthDate(new Date());
monkey.setStaff(new GoldRingedStaff());
try {
Monkey cloneMonkey = (Monkey) monkey.clone();
System.out.println("大圣本尊的生日是:" + monkey.getBirthDate());
System.out.println("克隆的大圣的生日是:" + monkey.getBirthDate());
System.out.println("大圣本尊跟克隆的大圣是否為同一個對象 " + (monkey == cloneMonkey));
System.out.println("大圣本尊持有的金箍棒跟克隆的大圣持有的金箍棒是否為同一個對象备埃? " + (monkey.getStaff() == cloneMonkey.getStaff()));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
程序輸出結(jié)果:
????可以看出姓惑,首先,復制的大圣本尊具有和原始的大圣本尊對象一樣的birthDate按脚,而本尊對象不相等于毙,這表明他們二者是克隆關(guān)系;其次辅搬,復制的大圣本尊所持有的金箍棒和原始的大圣本尊所持有的金箍棒為同一個對象唯沮。這表明二者所持有的金箍棒根本是一根,而不是兩根。
深克隆
public class GoldRingedStaff implements Serializable{
private float height = 100.0f;
private float diameter = 10.0f;
/**
* 增長行為介蛉,每次調(diào)用長度和半徑增加一倍
*/
public void grow() {
this.diameter *= 2;
this.height *= 2;
}
/**
* 縮小行為萌庆,每次調(diào)用長度和半徑減少一半
*/
public void shrink() {
this.diameter /= 2;
this.height /= 2;
}
}
public class Monkey implements Cloneable, Serializable {
private int height;
private int weight;
private Date birthDate;
private GoldRingedStaff staff;
@Override
protected Object clone() throws CloneNotSupportedException {
Monkey temp = null;
try {
temp = (Monkey) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
return temp;
}
}
public Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public GoldRingedStaff getStaff() {
return staff;
}
public void setStaff(GoldRingedStaff staff) {
this.staff = staff;
}
@Override
public String toString() {
return "Monkey{" +
"height=" + height +
", weight=" + weight +
", birthDate=" + birthDate +
", staff=" + staff +
'}';
}
}
public class Client {
public static void main(String[] args) {
Monkey monkey = new Monkey();
monkey.setHeight(170);
monkey.setWeight(100);
monkey.setBirthDate(new Date());
monkey.setStaff(new GoldRingedStaff());
try {
Monkey cloneMonkey = (Monkey) monkey.deepClone();
System.out.println("大圣本尊的生日是:" + monkey.getBirthDate());
System.out.println("克隆的大圣的生日是:" + monkey.getBirthDate());
System.out.println("大圣本尊跟克隆的大圣是否為同一個對象 " + (monkey == cloneMonkey));
System.out.println("大圣本尊持有的金箍棒跟克隆的大圣持有的金箍棒是否為同一個對象? " + (monkey.getStaff() == cloneMonkey.getStaff()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
程序輸出結(jié)果:
????從運行的結(jié)果可以看出币旧,大圣的金箍棒和他的身外之身的金箍棒是不同的對象践险。這是因為使用了深克隆,從而把大圣本尊所引用的對象也都復制了一遍佳恬,其中也包括金箍棒捏境。