备忘录模式
备忘录模式(Memento),在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存着这个状态。这样以后就可将该对象恢复到原先保存的状态。
这种模式的应用比较常见,例如游戏存档、撤销、浏览器中的后退和数据库中的事务管理等等。其核心类图如下
Originator:源发器类,负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态
Memento:备忘录类,负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento
Caretaker:备忘录类的管理者,负责保存好备忘录的Memento
# 优缺点
优点是给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。同时实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点是消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
# 案例代码
以游戏存档为例,看一下如何用备忘录模式实现。案例来自网络,博客的代码貌似是Java,但是并不妨碍
博客地址:https://www.cnblogs.com/adamjwh/p/11018268.html
program Memento;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
// 类型前置声明
TRole = class;
// 备忘录类
TRoleMemento = class
private
// 生命力
FVitality: Integer;
// 攻击力
FAggressivity: Integer;
// 防御力
FDefensivePower: Integer;
public
property Vitality: Integer read FVitality write FVitality;
property Aggressivity: Integer read FAggressivity write FAggressivity;
property DefensivePower: Integer read FDefensivePower write FDefensivePower;
// 重写构造
constructor Create(Role: TRole); overload;
end;
// 备忘录管理者类
TRoleStateCaretaker = class
private
// 这里为了降低学习成本仅仅保存了备忘录中的一个节点,如果需要可以使用容器对象
FRoleMemento: TRoleMemento;
public
property RoleMemento: TRoleMemento read FRoleMemento write FRoleMemento;
end;
// 游戏的角色类即源发器类
TRole = class
private
// 生命力
FVitality: Integer;
// 攻击力
FAggressivity: Integer;
// 防御力
FDefensivePower: Integer;
public
property Vitality: Integer read FVitality write FVitality;
property Aggressivity: Integer read FAggressivity write FAggressivity;
property DefensivePower: Integer read FDefensivePower write FDefensivePower;
// 存档
function RoleMemento(): TRoleMemento;
// 恢复
procedure RoleRecovery(RoleMemento: TRoleMemento);
end;
{ TRole }
function TRole.RoleMemento: TRoleMemento;
begin
Result := TRoleMemento.Create(Self);
end;
procedure TRole.RoleRecovery(RoleMemento: TRoleMemento);
begin
Self.Vitality := RoleMemento.Vitality;
Self.Aggressivity := RoleMemento.Aggressivity;
Self.DefensivePower := RoleMemento.DefensivePower;
end;
{ TRoleMemento }
constructor TRoleMemento.Create(Role: TRole);
begin
Self.Vitality := Role.Vitality;
Self.Aggressivity := Role.Aggressivity;
Self.DefensivePower := Role.DefensivePower;
end;
// 测试代码
begin
try
var
Role := TRole.Create;
// 初始化角色的状态信息
Role.Vitality := 10;
Role.Aggressivity := 2;
Role.DefensivePower := 4;
Writeln('角色初始状态:生命力:', Role.Vitality, ',攻击力:', Role.Aggressivity, ',防御力:', Role.DefensivePower);
// 存档
var
RoleStateCaretaker := TRoleStateCaretaker.Create();
RoleStateCaretaker.RoleMemento := Role.RoleMemento;
// 变更角色状态信息
Role.Vitality := 5;
Role.Aggressivity := 1;
Role.DefensivePower := 2;
Writeln('角色打怪之后状态:生命力:', Role.Vitality, ',攻击力:', Role.Aggressivity, ',防御力:', Role.DefensivePower);
// 打坐或者嗑药之后恢复状态
Role.RoleRecovery(RoleStateCaretaker.RoleMemento);
Writeln('角色恢复之后状态:生命力:', Role.Vitality, ',攻击力:', Role.Aggressivity, ',防御力:', Role.DefensivePower);
Readln;
except
on E: Exception do Writeln(E.ClassName, ': ', E.Message);
end;
end.
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
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