1. 程式人生 > 其它 >設計模式(十八)備忘錄模式

設計模式(十八)備忘錄模式

1、引入

  1. 瀏覽器回退:瀏覽器一般有瀏覽記錄,當我們在一個網頁上點選幾次連結之後,可在左上角點選左箭頭回退到上一次的頁面,然後也可以點選右箭頭重新回到當前頁面;
  2. 資料庫備份與還原:一般的資料庫都支援備份與還原操作,備份即將當錢已有的資料或者記錄保留,還原即將已經保留的資料恢復到對應的表中;
  3. 編輯器撤銷與重做:在編輯器上編輯文字,寫錯時可以按快捷鍵Ctrl+Z撤銷,撤銷後可以按Ctrl+y重做;
  4. 虛擬機器生成快照與恢復:虛擬機器可以生成一個快照,當虛擬機發生錯誤時可以恢復到快照的樣子;
  5. GIT版本管理:Git是最常見的版本管理軟體,每提交一個新版本,實際上Git就會把他們自動串成一條時間線,每個版本都有一個版本號,使用git reset --hard 版本號,即可回到指定的版本,讓程式碼時刻穿梭回到過去某個歷史時刻;
  6. 棋牌遊戲悔棋:在棋牌遊戲中,有時下快了可以悔棋,回退到上一步重新下。

2、定義:在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣可以在未來將物件恢復到原先儲存的狀態。它是一種物件行為型模式,其別名為Token。

3、角色分析

(1)Originator(原發器):它是一個普通類,可以建立一個備忘錄,並存儲它的當前內部狀態,也可以使用備忘錄來恢復其內部狀態,一般將需要儲存內部狀態的類設計為原發器;

(2)Memento(備忘錄):儲存原發器的內部狀態,根據原發器來決定儲存哪些內部狀態。備忘錄的設計一般可以參考原發器的設計,根據實際需要確定備忘錄類中的屬性。需要注意的是,除了原發器本身與負責人類之外,備忘錄物件不能直接供其他類使用,原發器的設計在不同的程式語言中實現機制會有所不同;

(3)Caretaker(負責人):負責人又稱為管理者,它負責儲存備忘錄,但是不能對備忘錄的內容進行操作或檢查。在負責人類中可以儲存一個或多個備忘錄物件,它只負責儲存物件,而不能修改物件,也無須知道物件的實現細節。

  備忘錄模式的核心是備忘錄類以及用於管理備忘錄的負責人類的設計。

4、程式碼例項

/**
 * @author it-小林
 * @desc  棋子類 原發器角色
 * @date 2021年09月18日 15:10
 */
public class Chessman {

    private String label;

    private Integer x;

    
private Integer y; public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public Integer getX() { return x; } public void setX(Integer x) { this.x = x; } public Integer getY() { return y; } public void setY(Integer y) { this.y = y; } public Chessman(String label, Integer x, Integer y) { this.label = label; this.x = x; this.y = y; } /** * 儲存狀態 * @return */ public ChessmanMemento save(){ return new ChessmanMemento(this.label, this.x, this.y); } /** * 恢復 * @param chessmanMemento */ public void restore(ChessmanMemento chessmanMemento){ this.label = chessmanMemento.getLabel(); this.x = chessmanMemento.getX(); this.y = chessmanMemento.getY(); } /** * 輸出 */ public void show(){ System.out.println(String.format("棋子<%s>:當前位置為:<%d, %d>", this.getLabel(), this.getX(), this.getY())); } }
/**
 * @author it-小林
 * @desc   備忘錄角色
 * @date 2021年09月18日 15:12
 */
public class ChessmanMemento {

    private String label;

    private Integer x;

    private Integer y;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    public Integer getY() {
        return y;
    }

    public void setY(Integer y) {
        this.y = y;
    }

    public ChessmanMemento(String label, Integer x, Integer y) {
        this.label = label;
        this.x = x;
        this.y = y;
    }
}
/**
 * @author it-小林
 * @desc   負責人角色
 * @date 2021年09月18日 15:14
 */
public class MementoCaretaker {

    private List<ChessmanMemento> mementoList = new ArrayList<>();

    public ChessmanMemento getMemento(int i){
        return mementoList.get(i);
    }

    public void addMemento(ChessmanMemento chessmanMemento){
        mementoList.add(chessmanMemento);
    }


}
/**
 * @author it-小林
 * @desc   客戶端
 * @date 2021年09月18日 15:22
 */
public class Client {

    private static int index = -1;

    private static MementoCaretaker mementoCaretaker = new MementoCaretaker();

    public static void main(String args[]){
        Chessman chessman = new Chessman("車", 1, 1);
        play(chessman);
        chessman.setX(4);
        play(chessman);
        chessman.setY(5);
        play(chessman);
        undo(chessman, index);
        undo(chessman, index);
        redo(chessman, index);
        redo(chessman, index);

    }

    /**
     * 下棋,同時儲存備忘錄
     * @param chessman
     */
    public static void play(Chessman chessman){
        mementoCaretaker.addMemento(chessman.save());
        index++;
        chessman.show();
    }

    /**
     * 悔棋,撤銷到上一個備忘錄
     * @param chessman
     * @param i
     */
    public static void undo(Chessman chessman, int i){
        System.out.println("*******悔棋*********");
        index--;
        chessman.restore(mementoCaretaker.getMemento(i-1));
        chessman.show();
    }

    /**
     * 撤銷悔棋,恢復到下一個備忘錄
     * @param chessman
     * @param i
     */
    public static void redo(Chessman chessman, int i){
        System.out.println("*****撤銷悔棋*****");
        index++;
        chessman.restore(mementoCaretaker.getMemento(i + 1));
        chessman.show();
    }
}

5、優缺點

(1)優點

  • 它提供了一種狀態恢復的實現機制,使得使用者可以方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可以使用暫時儲存起來的備忘錄將狀態復原;
  • 備忘錄實現了對資訊的封裝,一個備忘錄物件是一種原發器物件狀態的表示,不會被其他程式碼所改動。備忘錄儲存了原發器的狀態,採用列表、堆疊等集合來儲存備忘錄物件可以實現多次撤銷操作。

(2)缺點

  • 資源消耗過大,如果需要儲存的原發器類的成員變數太多,就不可避免需要佔用大量的儲存空間,每儲存一次物件的狀態都需要消耗一定的系統資源。

6、使用場景

  • 儲存一個物件在某一個時刻的全部狀態或部分狀態,這樣以後需要時它能夠恢復到先前的狀態,實現撤銷操作;

  • 防止外界物件破壞一個物件歷史狀態的封裝性,避免將物件歷史狀態的實現細節暴露給外界物件。
如本文有侵權行為,請及時與本人聯絡,多多包涵! 小生初出茅廬,多多指教!

本文來自部落格園,作者:it-小林,轉載請註明原文連結:https://www.cnblogs.com/linruitao/p/15067863.html