备忘录
当需要让对象返回之前的状态时(例如,用户请求撤销),就使用备忘录模式。
储存系统关键对象的重要状态。
维护关键对象的封装。
典型情境
游戏保存关卡进度。
适用性
- 必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
- 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
结构
Memento - Memento存储Originator对象的内部状态。Originator根据需要决定Memento存储Originator的哪些内部状态。防止Originator以外的其他对象访问Memento。Memento实际上有两个接口,Caretaker智能看到Memento的窄接口——它只能将备忘录传递给其他对象。相反,Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本Memento的那个Originator访问本Memento的内部状态。
Originator - 创建一个Memento,用以记录当前时刻它的内部状态。使用Memento恢复内部状态。
Caretaker - 复杂保存好Memento。不能对Memento的内容进行操作或检查。
协作:
Caretaker向Originator请求一个Memento,保留一段时间后,将其送回给Originator,见交互图。有时管理者不会将Memento返回给Originator,因为Originator可能根本不需要退到先前的状态。
Memento是被动的。只有创建Memento的Originator会对它的状态进行赋值和检索。
示例
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
| #include <iostream>
class State { public: State(int d) : m_data(d){} int m_data; };
class Memento { public: virtual ~Memento(){} protected: friend class Originator; Memento(State s) : m_state(s){} void set_state(State s) { m_state = s; } State get_state() { return m_state; } private: State m_state; };
class Originator { public: Originator() : m_state(0){} Memento* create_memento() { return new Memento(m_state); } void set_memento(Memento* m) { if (!m) return; m_state = m->get_state(); } void process() { m_state.m_data += 10; } void print_state() { std::cout << "State: " << m_state.m_data << std::endl; } private: State m_state; };
class Caretaker { public: Caretaker(): m_originator(new Originator()){} void test() { if(!m_originator) return; m_originator->process(); std::cout << "Save: "<< std::endl; m_originator->print_state(); Memento* saved = m_originator->create_memento(); m_originator->process(); m_originator->process(); std::cout << "Later: "<< std::endl; m_originator->print_state(); m_originator->set_memento(saved); std::cout << "Restore: "<< std::endl; m_originator->print_state(); } protected: Originator* m_originator; };
int main() { Caretaker c; c.test(); system("Pause"); }
|
优点
- 将被储存的状态放在外面,不要和关键对象混在一起,这可以帮助维护内聚。
- 保持关键对象的数据封装。
- 提供了容易实现的恢复能力。
用途和缺点
- 备忘录用于储存状态。
- 使用备忘录的缺点:储存和恢复状态的过程可能相当耗时。
相关模式
- Command: 命令可使用备忘录来为可撤销的操作维护状态。
- Iterator: 备忘录可用于迭代。
[1] 设计模式:可复用面向对象软件的基础
[2] Head First设计模式