設計模式之備忘錄模式(Memento)


生活中大家可能都幻想過如果當初我沒怎么樣怎么樣就好了,當產生這樣的想法的時候一定是發生什么不如意的事。生活是現實的,時間也永遠不會回到做錯事之前的一剎那。軟件雖服務於生活,模擬生活卻也和生活有所不同,應用軟件時,我們可以比較容易的進行備忘,比如編輯word或者txt時,我們使用的快捷鍵ctrl+z。還有我們可能也對系統進行過備份吧~~~

1.初識備忘錄模式

實際上在軟件中備忘錄模式的使用時非常頻繁的,並且備忘錄模式又是比較簡單的一種設計模式,所以可以說是性價比很高。比如我們玩中國象棋的時候需要悔棋,瀏覽網頁時需要后退等等這些都是備忘錄模式的應用。但在這里我們想說的是關於游戲進度保存的問題。當我們玩一款單機游戲的時候,可能我們突然有什么事要關機出去一段時間,或者好不容打到關底了,但是boss打不過還要重玩,這是我們可能也希望在打最終boss之前保存一下,這些時候可能我們都希望使用備忘錄模式,利用備忘錄模式保存打boss之前的狀態,如果打boss不過呢,還可以恢復進度,從打boss之前的狀態開始重打~~~

Gof對備忘錄模式是這樣說的:

備忘錄模式(Memento):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣以后就可將該對象恢復到原先保存的狀態。

同樣的我們還看看備忘錄模式的結構圖:

從結構中看出備忘錄模式包含3個角色:

1.Originator(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻自身的內部狀態,並可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲自己的哪些內部狀態。

2.Memento(備忘錄):負責存儲Originator對象的內部狀態,並可以防止Originator以外的其他對象訪問備忘錄。備忘錄有兩個接口:Caretaker只能看到備忘錄的窄接口,他只能將備忘錄傳遞給其他對象。Originator卻可看到備忘錄的寬接口,允許它訪問返回到先前狀態所需要的所有數據。

3.Caretaker(管理者):負責備忘錄Memento,不能對Memento的內容進行訪問或者操作。

 
2.實現備忘錄模式

現在假設游戲角色中有兩個屬性,角色名稱和角色生命值,現在要進行的保存游戲角色的生命值,但是游戲角色名稱不用保存。我們來看看怎樣利用備忘錄模式的代碼實現:

View Code
 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 //角色備忘錄類
 7 class RoleStateMemento
 8 {
 9 private:
10     int vitality;
11 public:
12     RoleStateMemento(){};
13     RoleStateMemento(int vit)
14     {
15         this->vitality = vit;
16     }
17     int get_vitality()
18     {
19         return vitality;
20     }
21 };
22 
23 //游戲角色類
24 class GameRole
25 {
26 private:
27     int vitality;
28     string name;
29 public:
30     GameRole(int Vit,string Name)
31     {
32         this->vitality = Vit;
33         this->name = Name;
34     }
35     //保存進度,只保存了生命力(vitality)
36     RoleStateMemento save_state()
37     {
38         RoleStateMemento memento(vitality);
39         return memento;
40     }
41     //恢復進度
42     void role_state_recover(RoleStateMemento memento)
43     {
44         this->vitality = memento.get_vitality();
45     }
46     //展示游戲角色狀態
47     void show()
48     {
49         cout<<"游戲角色名稱: "<<name<<endl;
50         cout<<"游戲角色生命值: "<<vitality<<endl;
51     }
52     //大戰boss生命值變為0
53     void fight()
54     {
55         this->vitality = 0;
56     }
57 };
58 //角色狀態管理者類
59 class RoleStateCaretaker
60 {
61 private:
62     RoleStateMemento memento;
63 public:
64     void set_memento(RoleStateMemento mem)
65     {
66         this->memento = mem;
67     }
68     RoleStateMemento get_memento()
69     {
70         return this->memento;
71     }
72 };
73 
74 int main()
75 {
76     //大戰boss前
77     GameRole Vincent(100,"VincentChen");
78     Vincent.show();
79     //保存進度
80     RoleStateCaretaker StateManager;
81     StateManager.set_memento(Vincent.save_state());
82     //大戰boss
83     Vincent.fight();
84     cout<<endl<<"大戰boss后的狀態:"<<endl;
85     Vincent.show();
86     //恢復進度
87     Vincent.role_state_recover(StateManager.get_memento());
88     cout<<endl<<"恢復進度后的狀態:"<<endl;
89     Vincent.show();
90     return 0;
91 }

 運行結果:

小結:想一想如果不用備忘錄模式怎么實現呢?如果要保存進度就要在main函數(客戶端)中保存Vincent的vitality屬性的值,然后恢復進度就要將Vincent的citality屬性賦值為已經保存下來的那個值,這樣一來的話,main函數(客戶端)要知道GameRole類內部的細節,並且要完成的操作也很多,這時如果游戲角色加入一些要保存的屬性更改起來也不方便。想想備忘錄模式的好處吧~~~

 

3.使用備忘錄模式的好處和優缺點

使用備忘錄模式的場合:

(1)功能比較復雜的,但是需要維護或記錄屬性歷史的類。

(2)需要保存的屬性只是眾多屬性的一小部分時。

使用備忘錄模式的好處:

(1)有時一些發起人對象的內部信息必須保存在發起人對象以外的地方,但是必須要由發起人對象自己讀取,這時使用備忘錄模式可以把復雜的發起人內部信息對其他的對象屏蔽起來,從而可以恰當地保持封裝的邊界。

(2)本模式簡化了發起人類。發起人不再需要管理和保存其內部狀態的一個個版本,客戶端可以自行管理他們所需要的這些狀態的版本。

(3)當發起人角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時存儲起來的備忘錄將狀態復原。

使用備忘錄模式的缺點:

(1)如果發起人角色的狀態需要完整地存儲到備忘錄對象中,那么在資源消耗上面備忘錄對象會很昂貴。

(2)當負責人角色將一個備忘錄存儲起來的時候,負責人可能並不知道這個狀態會占用多大的存儲空間,從而無法提醒用戶一個操作是否很昂貴。

            


            學習中的一點總結,歡迎拍磚哦^^

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM