本文的主要內(nèi)容:
- 介紹備忘錄模式
- 示例
- 備忘錄模式總結(jié)
備忘錄模式
備忘錄模式經(jīng)潮┐眨可以遇到,譬如下面這些場景:
瀏覽器回退:瀏覽器一般有瀏覽記錄赘来,當(dāng)我們在一個網(wǎng)頁上點擊幾次鏈接之后现喳,可在左上角點擊左箭頭回退到上一次的頁面,然后也可以點擊右箭頭重新回到當(dāng)前頁面
數(shù)據(jù)庫備份與還原:一般的數(shù)據(jù)庫都支持備份與還原操作犬辰,備份即將當(dāng)前已有的數(shù)據(jù)或者記錄保留嗦篱,還原即將已經(jīng)保留的數(shù)據(jù)恢復(fù)到對應(yīng)的表中
編輯器撤銷與重做:在編輯器上編輯文字,寫錯時可以按快捷鍵
Ctrl + z
撤銷幌缝,撤銷后可以按Ctrl + y
重做虛擬機(jī)生成快照與恢復(fù):虛擬機(jī)可以生成一個快照灸促,當(dāng)虛擬機(jī)發(fā)生錯誤時可以恢復(fù)到快照的樣子
Git版本管理:Git是最常見的版本管理軟件,每提交一個新版本涵卵,實際上Git就會把它們自動串成一條時間線浴栽,每個版本都有一個版本號,使用
git reset --hard 版本號
即可回到指定的版本轿偎,讓代碼時空穿梭回到過去某個歷史時刻棋牌游戲悔棋:在棋牌游戲中典鸡,有時下快了可以悔棋,回退到上一步重新下
備忘錄模式(Memento Pattern):在不破壞封裝的前提下坏晦,捕獲一個對象的內(nèi)部狀態(tài)萝玷,并在該對象之外保存這個狀態(tài),這樣可以在以后將對象恢復(fù)到原先保存的狀態(tài)英遭。它是一種對象行為型模式间护,其別名為Token。
角色
Originator(原發(fā)器):它是一個普通類汁尺,可以創(chuàng)建一個備忘錄,并存儲它的當(dāng)前內(nèi)部狀態(tài)多律,也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài)痴突,一般將需要保存內(nèi)部狀態(tài)的類設(shè)計為原發(fā)器。
Memento(備忘錄):存儲原發(fā)器的內(nèi)部狀態(tài)狼荞,根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)辽装。備忘錄的設(shè)計一般可以參考原發(fā)器的設(shè)計,根據(jù)實際需要確定備忘錄類中的屬性相味。需要注意的是拾积,除了原發(fā)器本身與負(fù)責(zé)人類之外,備忘錄對象不能直接供其他類使用,原發(fā)器的設(shè)計在不同的編程語言中實現(xiàn)機(jī)制會有所不同拓巧。
Caretaker(負(fù)責(zé)人):負(fù)責(zé)人又稱為管理者斯碌,它負(fù)責(zé)保存?zhèn)渫洠遣荒軐渫浀膬?nèi)容進(jìn)行操作或檢查肛度。在負(fù)責(zé)人類中可以存儲一個或多個備忘錄對象傻唾,它只負(fù)責(zé)存儲對象,而不能修改對象承耿,也無須知道對象的實現(xiàn)細(xì)節(jié)冠骄。
備忘錄模式的核心是備忘錄類以及用于管理備忘錄的負(fù)責(zé)人類的設(shè)計。
示例
下棋例子加袋,可以下棋凛辣,悔棋,撤銷悔棋等
棋子類 Chessman
职烧,原發(fā)器角色
@Data
@AllArgsConstructor
class Chessman {
private String label;
private int x;
private int y;
//保存狀態(tài)
public ChessmanMemento save() {
return new ChessmanMemento(this.label, this.x, this.y);
}
//恢復(fù)狀態(tài)
public void restore(ChessmanMemento memento) {
this.label = memento.getLabel();
this.x = memento.getX();
this.y = memento.getY();
}
public void show() {
System.out.println(String.format("棋子<%s>:當(dāng)前位置為:<%d, %d>", this.getLabel(), this.getX(), this.getY()));
}
}
備忘錄角色 ChessmanMemento
@Data
@AllArgsConstructor
class ChessmanMemento {
private String label;
private int x;
private int y;
}
負(fù)責(zé)人角色 MementoCaretaker
class MementoCaretaker {
//定義一個集合來存儲備忘錄
private ArrayList mementolist = new ArrayList();
public ChessmanMemento getMemento(int i) {
return (ChessmanMemento) mementolist.get(i);
}
public void addMemento(ChessmanMemento memento) {
mementolist.add(memento);
}
}
棋子客戶端蟀给,維護(hù)了一個 MementoCaretaker
對象
class Client {
private static int index = -1;
private static MementoCaretaker mc = new MementoCaretaker();
public static void main(String args[]) {
Chessman chess = new Chessman("車", 1, 1);
play(chess);
chess.setY(4);
play(chess);
chess.setX(5);
play(chess);
undo(chess, index);
undo(chess, index);
redo(chess, index);
redo(chess, index);
}
//下棋,同時保存?zhèn)渫? public static void play(Chessman chess) {
mc.addMemento(chess.save());
index++;
chess.show();
}
//悔棋阳堕,撤銷到上一個備忘錄
public static void undo(Chessman chess, int i) {
System.out.println("******悔棋******");
index--;
chess.restore(mc.getMemento(i - 1));
chess.show();
}
//撤銷悔棋,恢復(fù)到下一個備忘錄
public static void redo(Chessman chess, int i) {
System.out.println("******撤銷悔棋******");
index++;
chess.restore(mc.getMemento(i + 1));
chess.show();
}
}
輸出如下择克,悔棋成功恬总,撤銷悔棋成功
棋子<車>:當(dāng)前位置為:<1, 1>
棋子<車>:當(dāng)前位置為:<1, 4>
棋子<車>:當(dāng)前位置為:<5, 4>
******悔棋******
棋子<車>:當(dāng)前位置為:<1, 4>
******悔棋******
棋子<車>:當(dāng)前位置為:<1, 1>
******撤銷悔棋******
棋子<車>:當(dāng)前位置為:<1, 4>
******撤銷悔棋******
棋子<車>:當(dāng)前位置為:<5, 4>
類圖如下
備忘錄模式總結(jié)
備忘錄模式的主要優(yōu)點如下:
它提供了一種狀態(tài)恢復(fù)的實現(xiàn)機(jī)制,使得用戶可以方便地回到一個特定的歷史步驟肚邢,當(dāng)新的狀態(tài)無效或者存在問題時壹堰,可以使用暫時存儲起來的備忘錄將狀態(tài)復(fù)原。
備忘錄實現(xiàn)了對信息的封裝骡湖,一個備忘錄對象是一種原發(fā)器對象狀態(tài)的表示贱纠,不會被其他代碼所改動。備忘錄保存了原發(fā)器的狀態(tài)响蕴,采用列表谆焊、堆棧等集合來存儲備忘錄對象可以實現(xiàn)多次撤銷操作。
備忘錄模式的主要缺點如下:
- 資源消耗過大浦夷,如果需要保存的原發(fā)器類的成員變量太多辖试,就不可避免需要占用大量的存儲空間,每保存一次對象的狀態(tài)都需要消耗一定的系統(tǒng)資源劈狐。
適用場景:
保存一個對象在某一個時刻的全部狀態(tài)或部分狀態(tài)罐孝,這樣以后需要時它能夠恢復(fù)到先前的狀態(tài),實現(xiàn)撤銷操作肥缔。
防止外界對象破壞一個對象歷史狀態(tài)的封裝性莲兢,避免將對象歷史狀態(tài)的實現(xiàn)細(xì)節(jié)暴露給外界對象。
由于JDK、Spring改艇、Mybatis中很少有備忘錄模式收班,也許 Spring webflow 中的 StateManageableMessageContext 接口算一個,但是真的很少見遣耍,所以這里不做典型應(yīng)用源碼分析
后記
歡迎評論闺阱、轉(zhuǎn)發(fā)、分享舵变,您的支持是我最大的動力
更多內(nèi)容可訪問我的個人博客:http://laijianfeng.org
關(guān)注【小旋鋒】微信公眾號酣溃,及時接收博文推送