設(shè)計模式 | 備忘錄模式及典型應(yīng)用

本文的主要內(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)注【小旋鋒】微信公眾號酣溃,及時接收博文推送

關(guān)注_小旋鋒_微信公眾號
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纪隙,隨后出現(xiàn)的幾起案子赊豌,更是在濱河造成了極大的恐慌,老刑警劉巖绵咱,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘饼,死亡現(xiàn)場離奇詭異,居然都是意外死亡悲伶,警方通過查閱死者的電腦和手機(jī)艾恼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來麸锉,“玉大人钠绍,你說我怎么就攤上這事』ǔ粒” “怎么了柳爽?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碱屁。 經(jīng)常有香客問我磷脯,道長,這世上最難降的妖魔是什么娩脾? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任赵誓,我火速辦了婚禮,結(jié)果婚禮上柿赊,老公的妹妹穿的比我還像新娘架曹。我一直安慰自己,他們只是感情好闹瞧,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布绑雄。 她就那樣靜靜地躺著,像睡著了一般奥邮。 火紅的嫁衣襯著肌膚如雪万牺。 梳的紋絲不亂的頭發(fā)上罗珍,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音脚粟,去河邊找鬼覆旱。 笑死,一個胖子當(dāng)著我的面吹牛核无,可吹牛的內(nèi)容都是我干的扣唱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼团南,長吁一口氣:“原來是場噩夢啊……” “哼噪沙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吐根,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤正歼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拷橘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體局义,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年冗疮,在試婚紗的時候發(fā)現(xiàn)自己被綠了萄唇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡术幔,死狀恐怖穷绵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情特愿,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布勾缭,位于F島的核電站,受9級特大地震影響俩由,放射性物質(zhì)發(fā)生泄漏毒嫡。R本人自食惡果不足惜幻梯,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碘梢。 院中可真熱鬧,春花似錦煞躬、人聲如沸肛鹏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芒珠,卻和暖如春桥狡,著一層夾襖步出監(jiān)牢的瞬間皱卓,已是汗流浹背裹芝。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留好爬,地道東北人局雄。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像存炮,于是被迫代替她去往敵國和親炬搭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容