我們經常遇到將一個類的狀態恢復到歷史版本的需求。比如一個記事本,想保存一個上N個狀態,通過Ctrl+Z可以恢復此前編輯內容。備忘錄就非常適合這種場景。此時發起者類,希望能夠將自身狀態保留在某處,而且不希望過多的暴露細節。為了上述目的,發起者會在某個時刻通過new出一個備忘類對象,並將該對象托管給管理者類。然後需要恢復狀態時,又從管理者類中獲取具體狀態,將自身恢復到某個狀態。
為了保持封裝性,備忘錄一般除了將自己暴露給發起者外(或者可以認為是發起者將自己狀態暴露給了備忘類),不為外部類提供任何訪問狀態相關接口。C++中這個通常是通過將自己所有接口都改成private(包括構造函數),然後將發起者設為自己的友元類來實現。
寫了個demo,可以支持多個版本:
[cpp]
/***************************************************************************
*
* Copyright (c) 2013 itegel.com, Inc. All Rights Reserved
*
**************************************************************************/
/**
* @file test_memento.cpp
* @author itegel
* @date 2013/06/08 11:29:08
* @brief
*
**/
#include <string>
#include <map>
#include <iostream>
using namespace std;
class State{
public:
int version;
string content;
};
class Memento{
public:
private:
friend class NoteBook;
Memento(State state) : _state(state){}
~Memento(){}
State * GetState(){
return &_state;
}
void SetState(State state){
_state = state;
}
State _state;
};
//originator
class NoteBook{
public:
NoteBook(int version, string content){
_state.version = version;
_state.content = content;
}
NoteBook(State & state):_state(state){
}
void SetMemento(Memento * mem){
_state = mem->_state;
}
Memento * GetMemento(){
return new Memento(_state);
}
void SetState(int version, string content){
_state.version = version;
_state.content = content;
}
int GetVersion(){
return _state.version;
}
void PrintState(){
cout<<"Note:\n\tversion:\t"<<_state.version<<endl<<"\tcontent:\t"<<_state.content<<endl;
}
private:
State _state;
};
class CareTaker{
public:
CareTaker(){}
void Save(int id, Memento * mem){
_memento_map[id] = mem;
}
Memento * GetMemento(int id){
return _memento_map[id];
}
private:
map<int, Memento *> _memento_map;
};
int main(){
NoteBook my_note(0, "");
CareTaker care_taker;
my_note.PrintState();
cout<<"Save State 0!"<<endl;
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 1st time!"<<endl;
my_note.SetState(1, "Hello world!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 2nd time!"<<endl;
my_note.SetState(2, "Hello World! It is the 2nd version!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 3rd time!"<<endl;
my_note.SetState(3, "Hello Itegel! It is the 3rd version!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Restore 2nd version:"<<endl;
Memento * mem = care_taker.GetMemento(2);
my_note.SetMemento(mem);
my_note.PrintState();
cout<<endl<<"Restore 0 version:"<<endl;
mem = care_taker.GetMemento(0);
my_note.SetMemento(mem);
my_note.PrintState();
return 0;
}
/***************************************************************************
*
* Copyright (c) 2013 itegel.com, Inc. All Rights Reserved
*
**************************************************************************/
/**
* @file test_memento.cpp
* @author itegel
* @date 2013/06/08 11:29:08
* @brief
*
**/
#include <string>
#include <map>
#include <iostream>
using namespace std;
class State{
public:
int version;
string content;
};
class Memento{
public:
private:
friend class NoteBook;
Memento(State state) : _state(state){}
~Memento(){}
State * GetState(){
return &_state;
}
void SetState(State state){
_state = state;
}
State _state;
};
//originator
class NoteBook{
public:
NoteBook(int version, string content){
_state.version = version;
_state.content = content;
}
NoteBook(State & state):_state(state){
}
void SetMemento(Memento * mem){
_state = mem->_state;
}
Memento * GetMemento(){
return new Memento(_state);
}
void SetState(int version, string content){
_state.version = version;
_state.content = content;
}
int GetVersion(){
return _state.version;
}
void PrintState(){
cout<<"Note:\n\tversion:\t"<<_state.version<<endl<<"\tcontent:\t"<<_state.content<<endl;
}
private:
State _state;
};
class CareTaker{
public:
CareTaker(){}
void Save(int id, Memento * mem){
_memento_map[id] = mem;
}
Memento * GetMemento(int id){
return _memento_map[id];
}
private:
map<int, Memento *> _memento_map;
};
int main(){
NoteBook my_note(0, "");
CareTaker care_taker;
my_note.PrintState();
cout<<"Save State 0!"<<endl;
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 1st time!"<<endl;
my_note.SetState(1, "Hello world!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 2nd time!"<<endl;
my_note.SetState(2, "Hello World! It is the 2nd version!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Edit for 3rd time!"<<endl;
my_note.SetState(3, "Hello Itegel! It is the 3rd version!");
my_note.PrintState();
care_taker.Save(my_note.GetVersion(), my_note.GetMemento());
cout<<endl<<"Restore 2nd version:"<<endl;
Memento * mem = care_taker.GetMemento(2);
my_note.SetMemento(mem);
my_note.PrintState();
cout<<endl<<"Restore 0 version:"<<endl;
mem = care_taker.GetMemento(0);
my_note.SetMemento(mem);
my_note.PrintState();
return 0;
}
執行結果:
[plain]
Note:
version: 0
content:
Save State 0!
Edit for 1st time!
Note:
version: 1
content: Hello world!
Edit for 2nd time!
Note:
version: 2
content: Hello World! It is the 2nd version!
Edit for 3rd time!
Note:
version: 3
content: Hello Itegel! It is the 3rd version!
Restore 2nd version:
Note:
version: 2
content: Hello World! It is the 2nd version!
Restore 0 version:
Note:
version: 0
content:
Note:
version: 0
content:
Save State 0!
Edit for 1st time!
Note:
version: 1
content: Hello world!
Edit for 2nd time!
Note:
version: 2
content: Hello World! It is the 2nd version!
Edit for 3rd time!
Note:
version: 3
content: Hello Itegel! It is the 3rd version!
Restore 2nd version:
Note:
version: 2
content: Hello World! It is the 2nd version!
Restore 0 version:
Note:
version: 0
content:
備忘錄模式使用場景應該比較固定,但是其實現可以很靈活。主要思想就是將自己的狀態托管給一個其他類,但是又不想過多的暴露自身信息,從而引入了備忘錄類。變化的地方應該是狀態成員的變化吧。此時只需要修改State類就好,其他其實可以做到不用修改(除了發起者關於這個新增狀態信息的維護)。
P.S. 到這兒23個設計模式中行為模式都寫過一些demo了。好久沒抽空寫東西了,發現其實寫代碼都有些生疏了。
這些天一直抽空寫一些demo,理解還不夠深,但是代碼都是自己一行一行敲進去的。這樣體會會深一些吧。其實很多時候,還是需要跟實際項目結合,理解會更深一些。個人體會是,程序員多寫代碼才是王道。