1 概述
备忘录模式(Memento Pattern),又叫Token模式,它提供了一种方式,来捕捉对象某一时刻的内部状态,并将其保存成备忘录(Memento),如此一来,对象可以根据此备忘录恢复到之前的状态。
2 备忘录模式
几乎所有的编辑器都支持撤销功能,这其实就是备忘录模式的例子,撤销操作,使得文本得以恢复到之前的状态。面向对象设计中,备忘录模式的实现一般需要三个角色:
- 发起人(Originator):主对象,提供将内容保存成备忘录,或者从备忘录恢复状态的功能。
- 备忘录(Memento):备忘录对象,保存了主对象的一些历史状态。同时,为了防止内容被破坏和修改,它可以实现一些访问控制逻辑。
- 负责人(Caretaker):负责保存备忘录对象。
3 案例
想必很多游戏玩家对Save-Load
大法都不陌生。在打boss之前,我们可以保存一下游戏状态,如果没有打赢,我们可以从之前保存的存档重来————典型的备忘录模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 发起人对象
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);
}
}
输出:
1
2
3
4
5
6
7
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 总结
备忘录模式可以在不破坏封装的前提下,捕获一个类的内部状态,并且在该对象之外保存该状态,保证该对象能够恢复到历史的某个状态。