备忘录(Memento)模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。
备忘录模式的用意是:在不破坏封装(encapsulation)的条件下,将一个对象的状态捕捉(Capture)住,并外部化(Externalize)存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
备忘录模式结构与角色:
备忘录(Memento)角色的责任:
1、 将发起人(Originator)对象的内部状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。
2、 备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。备忘录有两个等效的接口:
A) 窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录接口的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
B) 宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人(Originator)角色的责任:
1、 创建一个含有当前的内部状态的备忘录对象。
2、 使用备忘录对象存储其内部状态。
负责人(Caretaker)角色的责任:
1、 负责保存备忘录对象。
2、 不检查备忘录对象的内容。
备忘录角色对任何对象都提供宽接口,即备忘录角色的内部所存储的状态就对所有对象公开,叫做白箱备忘录模式。
白箱实现的优缺点:明显的好处是比较简单,因此常常用做教学目的。白箱实现的一个明显的缺点是破坏发起人状态的封装。
黑箱备忘录模式的实现:将Memento设成Originator的内部类,从而将Memento对象封装在Originator里面,在外部提供一个标识接口(MementoIF给Caretaker以及其他对象。
备忘录模式的优点:
1、 有时候一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取。这时,使用备忘录可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
2、 本模式简化了发起人(Originator)类。发起人不在需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
3、 当发起人角色的状态改变的时候,有可能这个状态无效。这时候就可以使用暂时存储起来的备忘录将状态复原。
备忘录模式的缺点:
1、 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2、 当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否会很昂贵。
3、 当发起人角色的状态改变的时候,有可能这个状态无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。
“自述历史”模式(History-On-Self Pattern)实际上就是备忘录模式的一个变种,此时发起人角色自己兼任责任人角色。
自述历史模式的缺点:无法实现发起人的多态性。
假如协议模式(备忘录模式的另一个变种):将发起人对象做一个拷贝,在拷贝上执行某个操作,检查这个拷贝的状态是否有效和自恰,如果检查结果是无效或者不自恰,那么扔掉这个拷贝,并触发异常处理程序;相反,如果检查结果是有效和自恰的,那么在原对象上执行这个操作。
假如协议模式的优点:可以保证发起人对象永远不会处于无效或不自恰的状态。
假如协议模式的缺点:
1、 操作必须执行两次,如果操作成功率比较低的话,比较划算,反之就不太划算。
在有些情况下,拷贝和原对象均有共享的数据,这时候无论修改拷贝还是修改原对象都会导致数据被修改,假如协议模式就没有意义了。