备忘录模式
在GOF的《设计模式:可复用面向对象软件的基础》一书中对备忘录模式是这样说的:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
有时有必要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现检查点和取消机制,而要实现这些机制,你必须事先将状态信息保存在某处,这样才能将对象恢复到它们先前的状态。如何实现这个将状态信息保存在某处呢?使用原型模式?由于对象通常封装了其部分或所有的状态信息,使得其状态不能被其他对象访问,也就不可能在该对象之外保存其状态了。由于原型模式总是返回对象的全部状态信息,同时原型模式使其状态能被其它对象访问,这样就违反了封装的原则,还可能有损应用的可靠性和可扩展性。
再拿上面的《仙剑奇侠传》进行分析,当我们在打大BOSS之前存档,此时就需要将对应的游戏场景,任务信息,人物信息等等状态存储起来;当赢得大BOSS之后,覆盖之前的存档时,就将之前的存档丢弃,新建立一个存档,保存当前的状态信息;如果打输了,恢复存档,就将之前的存档信息读取出来,还原到打大BOSS之前的游戏场景,重新开始打大BOSS。这里面就是使用的备忘录模式。
一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,而后者称为备忘录的原发器。当需要设置原发器的检查点时,取消操作机制会向原发器请求一个备忘录。原发器用描述当前状态的信息初始化该备忘录。只有原发器可以向备忘录中存取信息,备忘录对其他的对象是“不可见”的。
UML类图

Memento:备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态;防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者只能看到备忘录的窄接口————它只能将备忘录传递给其他对象。相反,原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成备忘录的那个原发器访问本备忘录的内部状态;
Originator:原发器创建一个备忘录,用以记录当前时刻它的内部状态;我们使用备忘录恢复内部状态;
Caretaker:负责保存好备忘录;但是,不能对备忘录的内容进行操作或检查。
备忘录模式是按照以下方式进行协作的:
管理器向原发器请求一个备忘录,保留一段时间后,将其送回给原发器;而有的时候管理者不会将备忘录返回给原发器,因为原发器可能根本不需要退到先前的状态。备忘录是被动的,只有创建备忘录的原发器会对它的状态进行赋值和检索,如下面的时序图:

使用场合
在以下情况下使用备忘录模式:
- 必须保存一个对象在某一个时刻的部分或完整状态,这样以后需要时它才能恢复到先前的状态;
- 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
代码实现
1 1 #include <iostream> 2 2 using namespace std; 3 3 4 4 struct State 5 5 { 6 6 wchar_t wcsState[260]; 7 7 }; 8 8 9 9 class Memento 10 10 { 11 11 public: 12 12 Memento(State *pState) : m_pState(pState){} 13 13 14 14 State *GetState() { return m_pState; } 15 15 16 16 private: 17 17 friend class Originator; 18 18 19 19 State *m_pState; 20 20 }; 21 21 22 22 class Originator 23 23 { 24 24 public: 25 25 Originator() : m_pState(NULL) {} 26 26 ~Originator() 27 27 { 28 28 // Delete the storage of the state 29 29 if (m_pState) 30 30 { 31 31 delete m_pState; 32 32 m_pState = NULL; 33 33 } 34 34 } 35 35 36 36 void SetMemento(Memento *pMemento); 37 37 Memento *CreateMemento(); 38 38 39 39 void SetValue(wchar_t *value) 40 40 { 41 41 memset(wcsValue, 0, 260 * sizeof(wchar_t)); 42 42 wcscpy_s(wcsValue, 260, value); 43 43 } 44 44 45 45 void PrintState() { wcout<<wcsValue<<endl; } 46 46 47 47 private: 48 48 State *m_pState; // To store the object's state 49 49 50 50 wchar_t wcsValue[260]; // This is the object's real data 51 51 }; 52 52 53 53 Memento *Originator::CreateMemento() 54 54 { 55 55 m_pState = new State; 56 56 if (m_pState == NULL) 57 57 { 58 58 return NULL; 59 59 } 60 60 61 61 Memento *pMemento = new Memento(m_pState); 62 62 63 63 wcscpy_s(m_pState->wcsState, 260, wcsValue); // Backup the value 64 64 return pMemento; 65 65 } 66 66 67 67 void Originator::SetMemento(Memento *pMemento) 68 68 { 69 69 m_pState = pMemento->GetState(); 70 70 71 71 // Recovery the data 72 72 memset(wcsValue, 0, 260 * sizeof(wchar_t)); 73 73 wcscpy_s(wcsValue, 260, m_pState->wcsState); 74 74 } 75 75 76 76 // Manager the Memento 77 77 class Caretaker 78 78 { 79 79 public: 80 80 Memento *GetMemento() { return m_pMemento; } 81 81 void SetMemnto(Memento *pMemento) 82 82 { 83 83 // Free the previous Memento 84 84 if (m_pMemento) 85 85 { 86 86 delete m_pMemento; 87 87 m_pMemento = NULL; 88 88 } 89 89 90 90 // Set the new Memento 91 91 m_pMemento = pMemento; 92 92 } 93 93 94 94 private: 95 95 Memento *m_pMemento; 96 96 }; 97 97 98 98 int main() 99 99 { 100100 Originator *pOriginator = new Originator(); 101101 pOriginator->SetValue(L"On"); 102102 pOriginator->PrintState(); 103103 104104 // Now I backup the state 105105 Caretaker *pCaretaker = new Caretaker(); 106106 pCaretaker->SetMemnto(pOriginator->CreateMemento()); 107107 108108 // Set the new state 109109 pOriginator->SetValue(L"Off"); 110110 pOriginator->PrintState(); 111111 112112 // Recovery to the old state 113113 pOriginator->SetMemento(pCaretaker->GetMemento()); 114114 pOriginator->PrintState(); 115115 116116 if (pCaretaker) 117117 { 118118 delete pCaretaker; 119119 } 120120 121121 if (pOriginator) 122122 { 123123 delete pOriginator; 124124 } 125125 126126 return 0; 127127 }
我再根据上面的《仙剑奇侠传》来完成备忘录模式,代码如下:
1 1 #include <iostream> 2 2 using namespace std; 3 3 4 4 class RoleStateMemento 5 5 { 6 6 public: 7 7 RoleStateMemento(unsigned iBlood, unsigned iAttack, unsigned iDefense) : m_iBlood(iBlood), m_iAttack(iAttack), m_iDefense(iDefense){} 8 8 9 9 private: 1010 friend class GameRole; 1111 1212 unsigned GetBloodValue() { return m_iBlood; } 1313 unsigned GetAttackValue() { return m_iAttack; } 1414 unsigned GetDefenseValue() { return m_iDefense; } 1515 1616 unsigned m_iBlood; // 生命力 1717 unsigned m_iAttack; // 攻击力 1818 unsigned m_iDefense; // 防御力 1919 }; 2020 2121 class GameRole 2222 { 2323 public: 2424 GameRole() : m_iBlood(100), m_iAttack(100), m_iDefense(100){} 2525 2626 // 存档 2727 RoleStateMemento *SaveState() { return new RoleStateMemento(m_iBlood, m_iAttack, m_iDefense); } 2828 2929 // 恢复存档 3030 void RecoveryState(RoleStateMemento *pRoleState) 3131 { 3232 m_iBlood = pRoleState->GetBloodValue(); 3333 m_iAttack = pRoleState->GetAttackValue(); 3434 m_iDefense = pRoleState->GetDefenseValue(); 3535 cout<<"Recovery..."<<endl; 3636 } 3737 3838 void ShowState() 3939 { 4040 cout<<"Blood:"<<m_iBlood<<endl; 4141 cout<<"Attack:"<<m_iAttack<<endl; 4242 cout<<"Defense:"<<m_iDefense<<endl; 4343 } 4444 4545 void Fight() 4646 { 4747 m_iBlood -= 100; 4848 m_iAttack -= 10; 4949 m_iDefense -= 20; 5050 5151 if (m_iBlood == 0) 5252 { 5353 cout<<"Game Over"<<endl; 5454 } 5555 } 5656 5757 private: 5858 unsigned m_iBlood; // 生命力 5959 unsigned m_iAttack; // 攻击力 6060 unsigned m_iDefense; // 防御力 6161 }; 6262 6363 class RoleStateCaretaker 6464 { 6565 public: 6666 void SetRoleStateMemento(RoleStateMemento *pRoleStateMemento) { m_pRoleStateMemento = pRoleStateMemento; } 6767 RoleStateMemento *GetRoleStateMemento() { return m_pRoleStateMemento; } 6868 6969 private: 7070 RoleStateMemento *m_pRoleStateMemento; 7171 }; 7272 7373 int main() 7474 { 7575 GameRole *pLiXY = new GameRole(); // 创建李逍遥这个角色 7676 pLiXY->ShowState(); // 显示初始的状态 7777 7878 // 存档 7979 RoleStateCaretaker *pRoleStateCaretaker = new RoleStateCaretaker(); 8080 pRoleStateCaretaker->SetRoleStateMemento(pLiXY->SaveState()); 8181 8282 // 开始打大BOSS 8383 pLiXY->Fight(); 8484 pLiXY->ShowState(); 8585 8686 // 读档,从新开始 8787 pLiXY->RecoveryState(pRoleStateCaretaker->GetRoleStateMemento()); 8888 pLiXY->ShowState(); 8989 9090 return 0; 9191 }
总结
备忘录模式在实际应用中也不少;我们在进行文档编辑时,经常使用的撤销操作。使用C++实现备忘录模式的关键点在于Originator类是Memento的友元类,这样就使得管理备忘录的Caretaker对象,以及其它对象都不能读取、设置备忘录,只有Originator类才能进行备忘录的读取和设置。由于备忘录主要是用于对对象的状态进行备份,实现了撤销操作,如果对象的状态数据很大很多时,在进行备忘时,就会很占用资源,这个是我们在实际开发时需要考虑的东西。结合之前的设计模式,在总结命令模式时,说到命令模式支持事物的回退,而这个就是依靠的备忘录模式来实现的。好了,备忘录模式就总结至此。希望对大家有用。