1 概述
備忘錄模式(Memento Pattern),又叫Token模式,它提供了一種方式,來捕捉對象某一時刻的內部狀態,並將其保存成備忘錄(Memento),如此一來,對象可以根據此備忘錄恢復到之前的狀態。
2 備忘錄模式
幾乎所有的編輯器都支持撤銷功能,這其實就是備忘錄模式的例子,撤銷操作,使得文本得以恢復到之前的狀態。面向對象設計中,備忘錄模式的實現一般需要三個角色:
- 發起人(Originator):主對象,提供將內容保存成備忘錄,或者從備忘錄恢復狀態的功能。
- 備忘錄(Memento):備忘錄對象,保存了主對象的一些歷史狀態。同時,爲了防止內容被破壞和修改,它可以實現一些訪問控制邏輯。
- 負責人(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 總結
備忘錄模式可以在不破壞封裝的前提下,捕獲一個類的內部狀態,並且在該對象之外保存該狀態,保證該對象能夠恢復到歷史的某個狀態。