原型模式
定義: 用原型實(shí)例指定創(chuàng)建對象的種類兜粘,并且通過拷貝這些原型創(chuàng)建新的對象申窘。
public class PrototypeClass implements Cloneable{
//覆寫父類Object方法
@Override
public PrototypeClass clone(){
PrototypeClass prototypeClass = null;
try {
prototypeClass = (PrototypeClass)super.clone();
} catch (CloneNotSupportedException e) {
//異常處理
}
return prototypeClass;
}
}
原型模式已經(jīng)與Java融為一體,大家可以隨手拿來使用孔轴。
資源性能優(yōu)化場景:
類初始化需要非常繁瑣的數(shù)據(jù)準(zhǔn)備剃法,需要消化非常多的資源,這個(gè)資源包括數(shù)據(jù)路鹰、硬件資源等贷洲。
一個(gè)對象多個(gè)修改者的場景:
一個(gè)對象需要提供給其他對象訪問,而且各個(gè)調(diào)用者可能都需要修改其值時(shí)悍引,可以考慮使用原型模式拷貝多個(gè)對象供調(diào)用者使用恩脂。
注意:
clone時(shí)構(gòu)造函數(shù)不會被執(zhí)行,Object類的clone方法的原理是從內(nèi)存中(具體地說就是堆內(nèi)存)以二進(jìn)制流的方式進(jìn)行拷貝趣斤,重新分配一個(gè)內(nèi)存塊俩块,那構(gòu)造函數(shù)沒有被執(zhí)行也是非常正常的了。
淺拷貝和深拷貝:
關(guān)于淺拷貝:
public class Thing implements Cloneable{
//定義一個(gè)私有變量
private ArrayList arrayList = new ArrayList();
@Override
public Thing clone(){
Thing thing=null;
try {
thing = (Thing)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
public void setValue(String value){
this.arrayList.add(value);
}
public ArrayList getValue(){
return this.arrayList;
}
}
Object類提供的方法clone只是拷貝本對象浓领,其對象內(nèi)部的數(shù)組玉凯、引用對象等都不拷貝,還是指向原生對象的內(nèi)部元素地址联贩,這種拷貝就叫做淺拷貝漫仆。
原始類型+裝箱類型+String 會被拷貝,但其他的內(nèi)部成員對象和數(shù)組不會拷貝泪幌,只會拷貝其引用盲厌。
關(guān)于深拷貝:
public class Thing implements Cloneable{
//定義一個(gè)私有變量
private ArrayList arrayList = new ArrayList();
@Override
public Thing clone(){
Thing thing=null;
try {
thing = (Thing)super.clone();
thing.arrayList = (ArrayList)this.arrayList.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
}
注意這里數(shù)組與ArrayList等clone,只會拷貝內(nèi)部元素的引用數(shù)組祸泪,并不會深拷貝數(shù)組內(nèi)部的元素吗浩。
關(guān)于一些其他的拷貝的姿勢:
1.序列化(List深拷貝)
public static List deepCopy(List src) throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
List dest = (List) in.readObject();
return dest;
}
2.System.arraycopy(數(shù)組的淺拷貝)
這個(gè)方法不是用java語言寫的,而是底層用c或者c++實(shí)現(xiàn)的没隘,因而速度會比較快懂扼。但是是淺拷貝
3.Arrays.copyOf(數(shù)組的淺拷貝)
調(diào)用的是System.arraycopy,如ArrayList的clone方法調(diào)用的就是Arrays.copyOf。
備忘錄模式
定義:在不破壞封裝性的前提下阀湿,捕獲一個(gè)對象的內(nèi)部狀態(tài)赶熟,并在該對象之外保存這個(gè)狀態(tài)。這樣以后就可將該對象恢復(fù)到原先保存的狀態(tài)陷嘴。
角色:
發(fā)起人角色:記錄當(dāng)前時(shí)刻的內(nèi)部狀態(tài)映砖,負(fù)責(zé)定義哪些屬于備份范圍的狀態(tài),負(fù)責(zé)創(chuàng)建和恢復(fù)備忘錄數(shù)據(jù)罩旋。
備忘錄角色:負(fù)責(zé)存儲發(fā)起人對象的內(nèi)部狀態(tài)啊央,在需要的時(shí)候提供發(fā)起人需要的內(nèi)部狀態(tài)眶诈。
Caretaker備忘錄管理員角色:對備忘錄進(jìn)行管理涨醋、保存和提供備忘錄。
//發(fā)起人角色
public class Originator {
//內(nèi)部狀態(tài)
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//創(chuàng)建一個(gè)備忘錄
public Memento createMemento(){
return new Memento(this.state);
}
//恢復(fù)一個(gè)備忘錄
public void restoreMemento(Memento _memento){
this.setState(_memento.getState());
}
}
//備忘錄角色
public class Memento {
//發(fā)起人的內(nèi)部狀態(tài)
private String state = "";
//構(gòu)造函數(shù)傳遞參數(shù)
public Memento(String _state){
this.state = _state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
//備忘錄管理員角色
public class Caretaker {
//備忘錄對象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class Client {
public static void main(String[] args) {
//定義出發(fā)起人
Originator originator = new Originator();
//定義出備忘錄管理員
Caretaker caretaker = new Caretaker();
//創(chuàng)建一個(gè)備忘錄
caretaker.setMemento(originator.createMemento());
//恢復(fù)一個(gè)備忘錄
originator.restoreMemento(caretaker.getMemento());
}
}
備忘錄模式變種:
1.clone 方式的備忘錄(發(fā)起者與備忘錄融合)
//發(fā)起者與備忘錄融合
public class Originator implements Cloneable{
//內(nèi)部狀態(tài)
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//創(chuàng)建一個(gè)備忘錄
public Originator createMemento(){
return this.clone();
}
//恢復(fù)一個(gè)備忘錄
public void restoreMemento(Originator _originator){
this.setState(_originator.getState());
}
//克隆當(dāng)前對象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
public class Caretaker {
//發(fā)起人對象
private Originator originator;
public Originator getOriginator() {
return originator;
}
public void setOriginator(Originator originator) {
this.originator = originator;
}
}
1.clone 方式的備忘錄(發(fā)起者與備忘錄和管理者 融合)
public class Originator implements Cloneable{
private Originator backup;
//內(nèi)部狀態(tài)
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//創(chuàng)建一個(gè)備忘錄
public void createMemento(){
this.backup = this.clone();
}
//恢復(fù)一個(gè)備忘錄
public void restoreMemento(){
//在進(jìn)行恢復(fù)前應(yīng)該進(jìn)行斷言逝撬,防止空指針
this.setState(this.backup.getState());
}
//克隆當(dāng)前對象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
提問浴骂?在該對象之外保存這個(gè)狀態(tài),是否矛盾宪潮?
現(xiàn)在我們來考慮一下原型模式深拷貝和淺拷貝的問題溯警,在復(fù)雜的場景下它會讓你的程序邏輯異常混亂狡相,出現(xiàn)錯(cuò)誤也很難跟蹤梯轻。因此Clone方式的備忘錄模式適用于較簡單的場景。
多狀態(tài)的時(shí)候需要怎么寫尽棕?
public class Caretaker {
//容納備忘錄的容器
private ArrayMap<String,Memento> memMap = new ArrayMap<String,Memento>();
public Memento getMemento(String idx) {
return memMap.get(idx);
}
public void setMemento(String idx,Memento memento) {
this.memMap.put(idx, memento);
}
}