設計模式,行爲模式之備忘錄模式

1 概述

備忘錄模式(Memento Pattern),又叫Token模式,它提供了一種方式,來捕捉對象某一時刻的內部狀態,並將其保存成備忘錄(Memento),如此一來,對象可以根據此備忘錄恢復到之前的狀態。

2 備忘錄模式

幾乎所有的編輯器都支持撤銷功能,這其實就是備忘錄模式的例子,撤銷操作,使得文本得以恢復到之前的狀態。面向對象設計中,備忘錄模式的實現一般需要三個角色:

  1. 發起人(Originator):主對象,提供將內容保存成備忘錄,或者從備忘錄恢復狀態的功能。
  2. 備忘錄(Memento):備忘錄對象,保存了主對象的一些歷史狀態。同時,爲了防止內容被破壞和修改,它可以實現一些訪問控制邏輯。
  3. 負責人(Caretaker):負責保存備忘錄對象。

3 案例

想必很多遊戲玩家對Save-Load大法都不陌生。在打boss之前,我們可以保存一下游戲狀態,如果沒有打贏,我們可以從之前保存的存檔重來————典型的備忘錄模式

// 發起人對象
public class Game {
    int gameStage = 1;
    int characterLevel = 1;
    int monsterKilled = 0;
    public void play() throws InterruptedException {
        System.out.println("Playing Game...");
        gameStage += 1;
        characterLevel += 5;
        monsterKilled += 10;
    }
    // 創建一個存檔(備忘錄)
    public GameArchive save() {
        System.out.println("game saved!");
        return new GameArchive(gameStage, characterLevel, monsterKilled);
    };
    // 從存檔恢復狀態
    public void restore(GameArchive archive) {
        System.out.println("game rollback!");
        gameStage = archive.getGameStage();
        characterLevel = archive.getCharacterLevel();
        monsterKilled = archive.getMonsterKilled();
    }
    @Override
    public String toString() {
        return "game stage is " + gameStage +
                ", character level is " + characterLevel +
                ", monster killed is " + monsterKilled;
    }
}
// 備忘錄對象,保存遊戲狀態
public class GameArchive {
    int gameStage = 1;
    int characterLevel = 1;
    int monsterKilled = 0;
    public GameArchive(int gameStage, int characterLevel, int monsterKilled) {
        this.gameStage = gameStage;
        this.characterLevel = characterLevel;
        this.monsterKilled = monsterKilled;
    }
    public int getGameStage() {
        return gameStage;
    }
    public int getCharacterLevel() {
        return characterLevel;
    }
    public int getMonsterKilled() {
        return monsterKilled;
    }
}
// 負責人對象,持有備忘錄
public class ArchiveManager {
    // 如果又多個存檔,將是個List<GameArchive>
    GameArchive archive;
    public GameArchive getArchive() {
        return archive;
    }
    public void setArchive(GameArchive archive) {
        this.archive = archive;
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        ArchiveManager manager = new ArchiveManager();
        Game dmc = new Game();
        dmc.play();
        System.out.println("Game init status: " + dmc);
        manager.setArchive(dmc.save());
        dmc.play();
        System.out.println("Game new status: " + dmc);
        dmc.restore(manager.getArchive());
        System.out.println("Game status after restore: " + dmc);
    }
}

輸出:

Playing Game...
Game init status: game stage is 2, character level is 6, monster killed is 10
game saved!
Playing Game...
Game new status: game stage is 3, character level is 11, monster killed is 20
game rollback!
Game status after restore: game stage is 2, character level is 6, monster killed is 10

將遊戲狀態保存爲GameArchive對象,後續可以方便地將遊戲進度回退。備忘錄模式可以說是所有遊戲的標配,它實現了信息的封裝,使得用戶不需要關心狀態的保存細節。一般情況下,備忘錄對象可以有多個,以供主對象選擇恢復到何時的狀態。但備忘錄不宜過多,否則可能會佔用過多的內存。
理論上來說,備忘錄對象都可以通過序列化(Serializable)來實現。

4 總結

備忘錄模式可以在不破壞封裝的前提下,捕獲一個類的內部狀態,並且在該對象之外保存該狀態,保證該對象能夠恢復到歷史的某個狀態。

文中例子的github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章