備忘錄模式(Memento)
備忘錄模式(Memento),在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外保存這個狀態。這樣以後就可將該物件恢復到原先保存的狀態。
使用場合?
備忘錄模式(Memento)比較適用於功能比較複雜的,但需要維護或記錄屬性歷史的類別,或者需要保存的屬性只是眾多屬性中的一小部分時,Originator可以根據保存的Memento資訊還原到前一狀態。
比如,遊戲角色類別,用來儲存角色的生命力、攻擊力、防禦力的資料。
當角色的狀態改變時,如果這個狀態無效,這時就可以使用暫時儲存起來的備忘錄將狀態復原。
結構
Originator發起人:負責建立一個備忘錄Memento,用以記錄當前前時刻它的內部狀態,並可以使用備忘錄恢復內部狀態。Originator可根據需要決定Memento儲存Originator的哪些內部狀態。Memento備忘錄:負責儲存Originator物件的內部狀態,並可防止Originator以外的其他物件存取Memento備忘錄。備忘錄有兩個介面,Caretaker只能看到備忘錄的窄介面,它只能將備忘錄傳遞給其他物件。Originator能夠看到一個寬介面,允許它存取恢復到先前狀態所需的所有資料。
把要保存的節給封裝在
Memento中,哪一天要更改保存的細節也不用影響用戶端了。
Caretaker管理者:負責保存好Memento備忘錄,不能對備忘錄的內容進行操作或檢查。
1
2
3
4
5
6
7
8
9
10
Originator 發起人:負責建立一個備忘錄Memento,用以記錄當前前時刻它的內部狀態,並可以使用備忘錄恢復內部狀態
+ State
+ SetMemento(in m:Memento)
+ CreateMemento()
Memento 備忘錄:負責儲存 Originator 物件的內部狀態,並可防止Originator以外的其他物件存取Memento備忘錄
+ State
Caretaker 管理者:負責保存好備忘錄 Memento
- Memento: Memento
程式碼
「遊戲角色」類別其實就是一個Originator
Originator發起人類別
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//發起人類別
class Originator {
//需要保存的屬性,可以有很多個
public string State { get; set; }
//建立備忘錄
public Memento CreateMemento() {
//將需要保存的資訊導入並實體化出一個 Memonto物件
return new Memento(State);
}
//恢復備忘錄,將Memento導入並將相關資料恢復
public void SetMemento(Memento memento) {
this.State = memento.state;
}
//顯示資料
public void Show() {
Console.WriteLine($"State = {this.State}");
}
}
Memento 備忘錄類別
1
2
3
4
5
6
7
8
9
10
//備忘錄類別
class Memento {
//需要保存的資料屬性
public string State { get; set; }
//建構函式,將相關資料導入
public Memento(string state ) {
this.State = state;
}
}
Caretaker 管理者類別
1
2
3
4
5
//管理者類別
class Caretaker {
//備忘錄
public Memento Memento { get; set; }
}
用戶端程式碼
1
2
3
4
5
6
7
8
9
10
11
12
//Originator的初始狀態為ON
Originator o = new Originator();
o.State = "on";
o.Show();
//保存狀態,由於有了很好的封裝,可以隱藏Originator的實現的細節
Caretaker c = new Caretaker();
c.Memento = o.CreateMemento();
//恢復原初始狀態
o.SetMemento(c.Memento);
o.Show();
缺點
如果狀態資料很大很多,那麼在資源消耗上會非常耗記憶體。
v1.0 儲存遊戲進度
這樣的寫法,整個遊戲角色的細節暴露給了用戶端,用戶端的職責就太大了,需要知道遊戲角色的生命力、攻擊力、防禦力這些細節,還要對它進行「備份」。以後需要增加新的資料,這部分就一定要修改了,例如:增加「魔法力」或修改現有的某種力、「生命力」要改為「經驗值」等等。
遊戲角色類別
遊戲角色類別,用來儲存角色的生命力、攻擊力、防禦力的資料
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
//遊戲角色類別,用來儲存角色的生命力、攻擊力、防禦力的資料
class GameRole {
public int Vitality { get; set; } //生命力
public int Attack { get; set;} //攻擊力
public int Defense { get; set;} //防禦力
//狀態顯示
public void StateDisplay() {
Console.WriteLine("角色當前狀態:");
Console.WriteLine($"生命力: {Vitality}");
Console.WriteLine($"攻擊力: {Attack}");
Console.WriteLine($"防禦力: {Defense}");
}
//獲得初始狀態
public void GetInit() {
//資料通常來自本機硬碟或遠端資料庫
Vitality = 100;
Attack = 100;
Defense = 100;
}
//戰鬥
public void Fight() {
//在與魔頭戰後,遊戲數據損耗為0
Vitality = 0;
Attack = 0;
Defense = 0;
}
}
用戶端調用
- 暴露實現細節。
- 用戶端的職責太大。
TODO: 把「遊戲角色」的存取狀態細節封裝起來,而且最好封裝在外部的類別當中,以體現職責分離。
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
//大戰魔頭前
GameRole o = new GameRole();
o.GetInit(); //獲得初始角色狀態
o.StateDisplay();
//保存進度 -->暴露實現細節
GameRole roleBackup = new GameRole(); //透過新實體來保存進度
roleBackup.Vitality = o.Vitality;
roleBackup.Attack = o.Attack;
roleBackup.Defense = o.Defense;
//大戰魔頭時,損耗嚴重
o.Fight(); //戰鬥後損耗嚴重數據為0
o.StateDisplay();
//恢復之前進度,重新再玩 -->暴露實現細節
o.Vitality = roleBackup.Vitality;
o.Attack = roleBackup.Attack;
o.Defense = roleBackup.Defense;
o.StateDisplay();
/* 執行結果
角色當前狀態:
生命力: 100
攻擊力: 100
防禦力: 100
角色當前狀態:
生命力: 0
攻擊力: 0
防禦力: 0
角色當前狀態:
生命力: 100
攻擊力: 100
防禦力: 100
*/
v2.0 儲存遊戲進度
結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
遊戲角色
+ 生命力: int
+ 攻擊力: int
+ 防禦力: int
+ 狀態查看()
+ 儲存角色狀態(): 角色狀態儲存箱
+ 恢復角色狀態(in memento: 角色狀態儲存箱)
角色狀態儲存箱
+ 生命力: int
+ 攻擊力: int
+ 防禦力: int
角色狀態管理者
+ memento: 角色狀態儲存箱
程式碼
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
//遊戲角色
class Role {
public int Vitality { get; set; } //生命力
public int Attack { get; set;} //攻擊力
public int Defense { get; set;} //防禦力
//狀態查看
public void StateDisplay() {
Console.WriteLine("角色當前狀態:");
Console.WriteLine($"生命力: {Vitality}");
Console.WriteLine($"攻擊力: {Attack}");
Console.WriteLine($"防禦力: {Defense}");
}
//儲存角色狀態
public RoleStateMemento SaveState() {
//將遊戲角色的三個狀態值透過實體化「角色狀態儲存箱」回傳
return new RoleStateMemento(Vitality, Attack, Defense);
}
//恢復角色狀態
public void RecoveryState(RoleStateMemento memento) {
//將外部的「角色狀態儲存箱」中的狀態值賦值給遊戲角色
this.Vitality = memento.Vitality;
this.Attack = memento.Attack;
this.Defense = memento.Defense;
}
//獲得初始狀態
public void GetInit() {
//資料通常來自本機硬碟或遠端資料庫
Vitality = 100;
Attack = 100;
Defense = 100;
}
//戰鬥
public void Fight() {
//在與魔頭戰後,遊戲數據損耗為0
Vitality = 0;
Attack = 0;
Defense = 0;
}
}
//角色狀態儲存箱
class RoleStateMemento {
public int Vitality { get; set; } //生命力
public int Attack { get; set;} //攻擊力
public int Defense { get; set;} //防禦力
public RoleStateMemento(int vitality, int attack, int defense) {
this.Vitality = vitality;
this.Attack = attack;
this.Defense = defense;
}
}
//角色狀態管理者
class RoleCaretaker {
//角色狀態儲存箱
public RoleStateMemento Memento {get; set;}
}
//用戶端程式碼
public static void Main()
{
//大戰魔頭前
Role role = new Role();
role.GetInit(); //獲得初始角色狀態
role.StateDisplay();
//保存進度
//保存進度時,由於封裝在Memento中,因此我們並不知道保存了哪些具體的角色資料
RoleCaretaker stateAdmin = new RoleCaretaker();
stateAdmin.Memento = role.SaveState();
//大戰魔頭時,損耗嚴重
role.Fight();
role.StateDisplay();
//恢復之前進度,重新再玩
role.RecoveryState(stateAdmin.Memento);
role.StateDisplay();
}
/* 執行結果
角色當前狀態:
生命力: 100
攻擊力: 100
防禦力: 100
角色當前狀態:
生命力: 0
攻擊力: 0
防禦力: 0
角色當前狀態:
生命力: 100
攻擊力: 100
防禦力: 100
*/