一、 备忘录(Memento)模式结构
备忘录对象是一个用来存储另外一个对象内部状态的快照(snapshot)的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来适合的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。
其结构图如下:
备忘录模式涉及三个角色:备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色。
备忘录角色:
(1) 将发起人对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。
(2) 备忘录可以保护其内容不被发起人对象之外的任何对象所读取。
备忘录有两个等效的接口:
窄接口:负责人(Caretaker)对象和其他除发起人对象之外的任何对象看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他对象。
宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人角色:
(1) 创建一个含有当前的内部状态的备忘录对象。(2)使用备忘录对象存储其内部状态。
负责人角色:
(1) 负责保存备忘录对象。(2)不检查备忘录对象的内容。
二、 实现
双重接口,就是同一个接口对某一个对象提供宽接口,而对另一些对象提供窄接口。根据编程语言的性能,双重接口的实现方式有所不同。在C++中可以通过接口设置成私有同时将提供宽接口的对象设置为该接口的友元类即可。在java中则要通过把该接口的实现作为提供宽接口的对象的内部类来达到。
可以将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口MementorIF(Memento类实现MementorIF接口)给Caretaker以及其他对象。这样,Originator类看到的是Mementor的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。
类图如下:
代码如下:
三、 备忘录模式在Java中的应用
J2EE框架中备忘录模式的应用
Java引擎提供Session对象,可以弥补HTTP协议的无状态缺点,存储用户的状态。当一个新的网络用户调用一张JSP网页或者Servlet时,Servlet引擎会创建一个对应于这个用户的Session对象,具体的技术细节可能是向客户端浏览器发送一个含有Session ID的Cookie,或者使用URL改写技术将Session ID 包括在URL中等。在一段有效时间里,同一个浏览器可以反复访问服务器,而Servlet引擎便可以使用这个Session ID来判断来访者应当与哪一个Session对象对应。Session和Cookie的使用都是备忘录模式,如下图所示:
四、 备忘录模式的优缺点
优点:
1、 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取。这时,使用备忘录可以把复杂的发起人内部信息对其他的对象屏弊起来,从而可以恰当地保持封装的边界。
2、 本模式简化了发起人类,发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理它们所需要的这些状态的版本。
3、 当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
缺点:
1、 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
2、 当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否会昂贵。