动机
在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。
- 比如,对象级别的redo,undo操作
如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
- 维持封装性的前提下,怎么样实现对象的状态
如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
代码
//需要保存Originator状态的对象class Originator{//状态有很多string state; //这里只存放了一个string//.... //可能还有很多别的属性,也要保存它的状态public:Originator() {}//创建一个备忘录(快照)Memento createMomento() {Memento m(state);return m;}//设置一个备忘录void setMomento(const Memento & m) {state = m.getState();}};//我们希望能够将Originator某个时刻的状态,拍成一个快照(内存快照、状态快照),把它存起来//所以,我们设计了一个Memento类,里面存的状态与Originator相同class Memento{//Memento内部存了很多状态,这些状态是与Originator相同的string state;//..//1. 如果有些状态是不需要做快照的,那也就不用存起来//2. 也可以不需要一一对应,Memento可能存的只是Originator某个状态的内存流、字符串public:Memento(const string & s) : state(s) {}//提供一些方法给Originator访问string getState() const { return state; }void setState(const string & s) { state = s; }};int main(){Originator orginator;//捕获对象状态,存储到备忘录Memento mem = orginator.createMomento();//改变orginator状态//...//从备忘录中恢复orginator.setMomento(memento);}
到具体业务当中,如果你可能要保存50次内存快照,那就要考虑性能问题了
- 比如,使用序列化技术,内存编码的技术,实现对快照高效的存储
模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。——《设计模式》GoF
- 不破坏
Originator的封装性 - 捕获对象内部状态:
orginator.createMomento - 在该对象之外保存这个状态:
Memento mem - 恢复状态:
orginator.setMomento(memento)
结构

Carewaker使用备忘录的人,比如例子中的main函数
要点总结
备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器的状态。
Memento模式的核心是信息隐藏,即Originator需要向外界隐藏信息,保持其封装性。但同时又需要将状态保持到外界(Memenoto)。
由于现代语言运行时(如C#、Java等)都具有相当的对象序列化支持,因此往往采用效率较高、又较容器正确实现的序列化方案来实现Memento模式。
- 有了序列化技术的支持,目前已经不需要使用面向对象的方式来实现了。不需要再定义一个
Memento类,来对Originator类做一一映射了,这样是很低效的 - 但思想是什么。就是要对外界做信息隐藏,不能将
Originator的信息暴露给了外界,从而保持Originator的封装性。在这个基础上,再要保存Originator的快照
总结
GoF设计模式是94年问世的,有一些设计模式当下已经不流形了。有一些设计模式在当时的内存环境、语言条件,是很适用的。但在当前的环境下,可能在实践上就显得非常非常的容易,而且也变得不再适用了。
如果这个Originator是一个很复杂的对象,里面套了好几层的指针,那备忘录方式就很鸡肋了。当下可以使用序列化的方式来做。
