模式定義:
在不破環(huán)封裝行性的前提下段直,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)溶诞。這樣就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài)鸯檬。
模式場(chǎng)景
- 錄入大批人員資料的時(shí)候。正在錄入當(dāng)前人資料時(shí)螺垢,發(fā)現(xiàn)上一個(gè)人錄錯(cuò)了喧务,此時(shí)需要恢復(fù)到上一個(gè)人的資料,進(jìn)行修改枉圃。
- Word編輯時(shí)功茴,忽然斷電或電腦死機(jī),再打開時(shí)孽亲,可以看到Word提示你恢復(fù)到以前的文檔坎穿。
模式結(jié)構(gòu)
Memento:備忘錄。主要用來(lái)存儲(chǔ)原發(fā)器對(duì)象的內(nèi)部狀態(tài)墨林,但是具體需要存儲(chǔ)哪些數(shù)據(jù)是由原發(fā)器對(duì)象來(lái)決定的赁酝。另外備忘錄應(yīng)該只能由原發(fā)器對(duì)象來(lái)訪問(wèn)它內(nèi)部的數(shù)據(jù),原發(fā)器外部的對(duì)象不應(yīng)該能訪問(wèn)到備忘錄對(duì)象的內(nèi)部數(shù)據(jù)旭等。
Originator:原發(fā)器酌呆。使用備忘錄來(lái)保存某個(gè)時(shí)刻原發(fā)器自身的狀態(tài),也可以使用備忘錄來(lái)恢復(fù)內(nèi)部狀態(tài)搔耕。
- Caretaker:備忘錄管理者隙袁,或者稱為備忘錄負(fù)責(zé)人。主要負(fù)責(zé)保存?zhèn)渫泴?duì)象弃榨,但是不能對(duì)備忘錄對(duì)象的內(nèi)容進(jìn)行操作或檢查菩收。
代碼實(shí)現(xiàn)
UML圖
源碼
public interface FlowAMockMemento {
}
public class FlowAMock {
private String flowName;
private int tempResult;
private String tempState;
public FlowAMock(String flowName) {
this.flowName = flowName;
}
public void runPhaseOne(){
tempResult = 3;
tempState = "phaseOne";
}
public void schema1(){
this.tempState += ",Schema1";
System.out.println(this.tempState+":now run " + tempResult);
this.tempResult += 11;
}
public void schema2(){
this.tempState += ",Schema2";
System.out.println(this.tempState+":now run " + tempResult);
this.tempResult += 22;
}
public FlowAMockMemento createMemento(){
return new MementoImpl(this.tempResult,this.tempState);
}
public void setMemento(FlowAMockMemento memento){
MementoImpl mementoImpl = (MementoImpl) memento;
this.tempResult = mementoImpl.getTempResult();
this.tempState = mementoImpl.getTempState();
}
/**
* 真正的備忘錄對(duì)象,實(shí)現(xiàn)備忘錄窄接口
* 實(shí)現(xiàn)成為私有內(nèi)部類
*/
private static class MementoImpl implements FlowAMockMemento{
@Getter
private int tempResult;
@Getter
private String tempState;
public MementoImpl(int tempResult, String tempState) {
this.tempResult = tempResult;
this.tempState = tempState;
}
}
}
@Data
public class FlowAMementoCareTaker{
private FlowAMockMemento memento;
}
public class Client {
public static void main(String[] args) {
FlowAMock mock = new FlowAMock("testFlow");
mock.runPhaseOne();
FlowAMementoCareTaker careTaker = new FlowAMementoCareTaker();
FlowAMockMemento memento = mock.createMemento();
careTaker.setMemento(memento);
mock.schema1();
mock.setMemento(memento);
mock.schema2();
}
}
模式的優(yōu)缺點(diǎn)
模式的優(yōu)點(diǎn)
-
更好的封裝性
備忘錄模式通過(guò)使用備忘錄對(duì)象,來(lái)封裝原發(fā)器對(duì)象的內(nèi)部狀態(tài)鲸睛,雖然這個(gè)對(duì)象是保存在原發(fā)器對(duì)象的外部娜饵,但是由于備忘錄對(duì)象的窄接口并不提供任何方法,這樣有效的保證了對(duì)原發(fā)器對(duì)象內(nèi)部狀態(tài)的封裝官辈,不把原發(fā)器對(duì)象的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)暴露給外部箱舞。
-
簡(jiǎn)化了原發(fā)器
備忘錄模式中遍坟,備忘錄對(duì)象被保存到原發(fā)器對(duì)象之外,讓客戶來(lái)管理他們請(qǐng)求的狀態(tài)晴股,從而讓原發(fā)器對(duì)象得到簡(jiǎn)化愿伴。
窄接口和寬接口
模式的缺點(diǎn)
-
可能會(huì)導(dǎo)致高開銷
備忘錄模式基本的功能,就是對(duì)備忘錄對(duì)象的存儲(chǔ)和恢復(fù)电湘,它的基本實(shí)現(xiàn)方式就是緩存?zhèn)渫泴?duì)象隔节。這樣一來(lái),如果需要緩存的數(shù)據(jù)量很大怎诫,或者是特別頻繁的創(chuàng)建備忘錄對(duì)象,開銷是很大的刽虹。
思考
模式本質(zhì):保存和恢復(fù)內(nèi)部狀態(tài)
開發(fā)中的應(yīng)用場(chǎng)景:
棋類游戲呢诬,悔棋
普通軟件尚镰,撤銷操作
數(shù)據(jù)庫(kù)軟件中的,事務(wù)管理的狗唉,回滾操作
Photoshop軟件
相關(guān)模式
- 備忘錄模式和命令模式
這兩個(gè)模式可以組合使用涡真。
命令模式實(shí)現(xiàn)中,在實(shí)現(xiàn)命令的撤銷和重做的時(shí)候哆料,可以使用備忘錄模式,在命令操作的時(shí)候記錄下操作前后的狀態(tài)杏节,然后在命令撤銷和重做的時(shí)候,直接使用相應(yīng)的備忘錄對(duì)象來(lái)恢復(fù)狀態(tài)就可以了奋渔。
在這種撤銷的執(zhí)行順序和重做執(zhí)行順序可控的情況下壮啊,備忘錄對(duì)象還可以采用增量式記錄的方式嫉鲸,可以減少緩存的數(shù)據(jù)量。
- 備忘錄模式和原型模式
這兩個(gè)模式可以組合使用歹啼。
在原發(fā)器對(duì)象創(chuàng)建備忘錄對(duì)象的時(shí)候玄渗,如果原發(fā)器對(duì)象中全部或者大部分的狀態(tài)都需要保存减江,一個(gè)簡(jiǎn)潔的方式就是直接克隆一個(gè)原發(fā)器對(duì)象。也就是說(shuō)捻爷,這個(gè)時(shí)候備忘錄對(duì)象里面存放的是一個(gè)原發(fā)器對(duì)象的實(shí)例。