Design Patterns(十八) Memento

Memento pattern is a behavioral design pattern. Memento pattern is used to restore state of an object to a previous state. As your application is progressing, you may want to save checkpoints in your application and restore back to those checkpoints later.

前言

    备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。

备忘录模式

基本介绍:

1) 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态;
2) 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作;
3) 备忘录模式属于行为型模式。

角色介绍:

1)Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器;
2)Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同;
3)Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。

备忘录模式-UML图:


代码:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @Auther: Arsenal
* @Date: 2020-03-21 12:13
* @Description: 备忘录模式
*/
public class MementoDemo {
public static void main(String[] args) {
GameCSGO csgo = new GameCSGO();
csgo.playGame();
try {
//玩了一会
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//暂停游戏
csgo.exitGame();
GameManager.getGameManager().saveGameInfo("备忘录模式", csgo.saveGameInfo());

//恢复游戏
csgo.loadGame(GameManager.getGameManager().getGameInfo("备忘录模式"));
}
}

/**
* Originator角色
*/
class GameCSGO {
/**
* 游戏开始时间
*/
private int time;
/**
* 游戏人头数
*/
private int killPeople;
/**
* 是否暂停
*/
private boolean isPause = false;

/**
* 玩游戏
*/
public void playGame() {
new Thread(new Runnable() {

@Override
public void run() {
while (!isPause) {
System.out.println("游戏开始了:" + time + "分钟,人头数:" + killPeople);
time++;
killPeople++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}

/**
* 结束游戏
*/
public void exitGame() {
isPause = true;
System.out.println("=====结束游戏=====");
System.out.println("游戏开始了:" + time + "分钟,人头数:" + killPeople);
System.out.println("===============");
}

/**
* 保存获取当前游戏信息
* @return
*/
public GameInfo saveGameInfo() {
return new GameInfo(time, killPeople);
}

/**
* 重新加载游戏
* @param gameInfo
*/
public void loadGame(GameInfo gameInfo) {
time = gameInfo.getTime();
killPeople = gameInfo.getKillPeople();
System.out.println("=====恢复游戏=====");
System.out.println("游戏开始了:" + time + "分钟,人头数:" + killPeople);
System.out.println("===============");
isPause = false;
}
}


/**
* Memento角色
*/
class GameInfo {
private int time;
private int killPeople;

public GameInfo(int time, int killPeople) {
super();
this.time = time;
this.killPeople = killPeople;
}

public int getTime() {
return time;
}

public void setTime(int time) {
this.time = time;
}

public int getKillPeople() {
return killPeople;
}

public void setKillPeople(int killPeople) {
this.killPeople = killPeople;
}
}

/**
* Caretaker角色
*/
class GameManager {
private Map<String, GameInfo> gameMap;
private static GameManager instance;

private GameManager() {
gameMap = new ConcurrentHashMap<>();
}

public static GameManager getGameManager() {
if (instance == null) {
synchronized (GameManager.class) {
if (instance == null) {
instance = new GameManager();
}
}
}
return instance;
}

/**
* 保存游戏信息
* @param name
* @param gameInfo
*/
public void saveGameInfo(String name, GameInfo gameInfo) {
gameMap.put(name, gameInfo);
}

/**
* 读取游戏信息
* @param name
* @return
*/
public GameInfo getGameInfo(String name) {
return gameMap.get(name);
}
}

总结

备忘录模式的注意事项和细节:

1) 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;
2) 实现了信息的封装,使得用户不需要关心状态的保存细节;
3) 如果类的成员变量过多,势必会占用比 较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意;
4) 适用的应用场景:1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z;
4、IE 中的后退。 4、数据库的事务管理;
5) 为了节约内存,备忘录模式可以和原型模式配合使用。

延伸

    备忘录模式
    备忘录模式-菜鸟教程
    设计模式 | 备忘录模式及典型应用
    Design Patterns - Memento Pattern
    尚硅谷Java设计模式,韩顺平图解java设计模式

Content
  1. 1. 前言
  2. 2. 备忘录模式
  3. 3. 总结
  4. 4. 延伸