程序設計模式(十六) C++ 備忘錄( Memento)模式

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

場景:

(1)我們玩單機遊戲的時候總會遇到老婆大人的各位事情,一會去買瓶醋了,一會去打個醬油了,會耽誤我們玩遊戲的進程,但是此時我們能有“保存遊戲”這個寶貝,我們的主基地不會在我們打醬油的時候被對手拆掉。

    這“保存遊戲”的功能其實就是備忘錄模式的很好應用,她是在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以後就可以就該對象恢復到原先保存的狀態。這個其實也是我們的redo,undo所採用的模式。

(2)    我們玩單機遊戲的時候總會遇到老婆大人的各位事情,一會去買瓶醋了,一會去打個醬油了,會耽誤我們玩遊戲的進程,但是此時我們能有“保存遊戲”這個寶貝,我們的主基地不會在我們打醬油的時候被對手拆掉,這“保存遊戲”的功能其實就是備忘錄模式的很好應用.

思想:該模式主要用來實現類似我們在常見的編輯器中經常執行的Undo(Ctrl+Z) 操作。實際上就是在外部保持一組對象的某一時刻的狀態,並在需要的另一個時候將這組對象回覆到之前的狀態。

優點:

1)當發起人角色的狀態有改變時,有可能是個錯誤的改變,我們使用備忘錄模式就可以把這個錯誤改變還原。

2)備份的狀態是保存在發起人角色之外的,這樣,發起人角色就不需要對各個備份的狀態進行管理。

缺點:

1)如果備份的對象存在大量的信息或者創建、恢復操作非常頻繁,則可能造成很大的性能開銷。

類圖:



Originator:負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態。Originator可根據需要決定Memento存儲Originator的哪些內部狀態。

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

Caretaker:負責保存好備忘錄Memento,不能對備忘錄的內容進行操作或檢查。

Memento模式中封裝的是需要保存的狀態,當需要恢復的時候才取出來進行恢復.原理很簡單,實現的時候需要注意一個地方:窄接口和寬接口.所謂的寬接口就是一般意義上的接口,把對外的接口作爲public成員;而窄接口反之,把接口作爲private成員,而把需要訪問這些接口函數的類作爲這個類的友元類,也就是說接口只暴露給了對這些接口感興趣的類,而不是暴露在外部.下面的實現就是窄實現的方法來實現的.

Memento模式比較適用於功能比較複雜的,但需要維護或記錄歷史屬性的類,或者需要保存的屬性只是衆多屬性中的一小部分時,Originator可以根據保存的Memento信息還原到前一狀態。

如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麼命令模式可以使用備忘錄模式來存儲可撤銷操作的狀態。

實現:該模式其實主要是一種對象狀態的暫存和回覆的思想。上面的UML圖是一種比較典型的實現方式:制定一 個專門用於保存類狀態的類,爲被保存狀態的類定義返回當前狀態類實例,和根據狀態類實例回覆對象狀態的接口。實際上也不必太拘泥於這個實現,簡單情形下, 我們完全可以利用任何的已有的對象持久化或者序列化機制來用一個字符串暫存對象的當前完整狀態。

實例:

Memento.h

 1 #ifndef _MEMENTO_H_
 2 #define _MEMENTO_H_
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 //負責存儲Originator對象的內部狀態,並可防止Originator以外的其他對象訪問備忘錄Memento。
 8 //備忘錄有兩個接口,Caretaker只能看到備忘錄的窄接口,它只能將備忘錄傳遞給其他對象。Originator能夠看到一個寬接口,允許它訪問返回到先前狀態所需的所有數據。
 9 class Memento
10 {
11 private:
12     //將Originator爲friend類,可以訪問內部信息,但是其他類不能訪問
13     friend class Originator;
14     Memento(const string& state);
15     ~Memento();
16     void SetState(const string& state);
17     string GetState();
18     string _state;
19 };
20 
21 //負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,並可使用備忘錄恢復內部狀態
22 class Originator
23 {
24 public:
25     Originator();
26     Originator(const string& state);
27     ~Originator();
28     void RestoreToMemento(Memento* pMemento);
29     Memento* CreateMemento();
30     void SetState(const string& state);
31     string GetState();
32     void show();
33 protected:
34 private:
35     string _state;
36 };
37 
38 //負責保存好備忘錄Memento,不能對備忘錄的內容進行操作或檢查
39 class Caretaker
40 {
41 public:
42     Caretaker();
43     ~Caretaker();
44     void SetMemento(Memento*);
45     Memento* GetMemento();
46 private:
47     Memento* _memento;
48 };
49 
50 #endif

Memento.cpp

 1 #include "Memento.h"
 2 #include <iostream>
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 Memento::Memento(const string& state)
 8 {
 9     this->_state = state;
10 }
11 
12 Memento::~Memento()
13 {}
14 
15 string Memento::GetState()
16 {
17     return this->_state;
18 }
19 
20 void Memento::SetState(const string& state)
21 {
22     this->_state = state;
23 }
24 
25 Originator::Originator()
26 {}
27 
28 Originator::Originator(const string& state)
29 {
30     this->_state = state;
31 }
32 
33 Originator::~Originator()
34 {}
35 
36 string Originator::GetState()
37 {
38     return this->_state;
39 }
40 
41 void Originator::show()
42 {
43     cout << this->_state << endl;
44 }
45 
46 void Originator::SetState(const string& state)
47 {
48     this->_state = state;
49 }
50 
51 Memento* Originator::CreateMemento()
52 {
53     return new Memento(this->_state);
54 }
55 
56 void Originator::RestoreToMemento(Memento* pMemento)
57 {
58     this->_state = pMemento->GetState();
59 }
60 
61 Caretaker::Caretaker()
62 {}
63 
64 Caretaker::~Caretaker()
65 {}
66 
67 Memento* Caretaker::GetMemento()
68 {
69     return this->_memento;
70 }
71 
72 void Caretaker::SetMemento(Memento* pMemento)
73 {
74     this->_memento = pMemento;
75 }

main.cpp

 1 #include "Memento.h"
 2 
 3 int main()
 4 {
 5     //初始化對象,狀態爲“Old”
 6     Originator* o = new Originator("Old");
 7     o->show();
 8 
 9     //建立並保存Memento
10     Caretaker* pTaker = new Caretaker();
11     pTaker->SetMemento(o->CreateMemento());
12 
13     //改變狀態
14     o->SetState("New");
15     o->show();
16 
17     //恢復狀態
18     o->RestoreToMemento(pTaker->GetMemento());
19     o->show();
20 
21     return 0;
22 }

結果如下:

Memento 模式的關鍵就是 friend class Originator;我們可以看到,Memento 的接口都聲明爲 private,而將 Originator 聲明爲 Memento 的友元類。我們將 Originator 的狀態保存在 Memento 類中,而將 Memento 接口 private 起來,也就達到了封裝的功效。
      在 Originator 類中我們提供了方法讓用戶後悔:RestoreToMemento(Memento* mt);我們可以 通過這個接口讓用戶後悔。在測試程序中,我們演示了這一點:Originator 的狀態由 old 變爲 new 最 後又回到了 old。

重構成本:低。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章