备忘录模式
模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这一状态。以后可以将对象恢复到原先保存的状态。备忘录模式在GOF95中被分类为行为型模式。
模式结构与说明
- Memento:备忘录接口,用来存放原发器对象某个瞬间的内部状态,该接口通常是空接口或者窄接口,不会声明任何获取原发器内部状态的方法
- MementoImpl:备忘录实现类,为了不破坏封装性,备忘录的实现类往往作为原发器私有内部类实现,备忘录的实例应该由原发器自己生成
- Originator:原发器,其状态需要保存和恢复,提供这两个接口
- CareTaker:备忘录管理者,负责保存和取回备忘录
备忘录模式的优点:
- 将被存储的状态放在外面,不和关键对象混在一起,提高了后者内聚性
- 保持了关键对象的数据封装
- 提供了容易实现的恢复能力
备忘录的缺点:
- 存储和恢复状态的过程可能比较耗时
- 如果存储的备忘录数量很大,可能导致不必要的开销
备忘录的适用场景:
- 需要持久化对象状态,以便未来恢复时
经典应用
Java的串行化机制
串行化机制可以认为是一种备忘录模式,任何实现java.io.Serializable接口的类都相当于Originator角色:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializableObject implements java.io.Serializable { private static final long serialVersionUID = 1L; private int field0; private String field1; //transient关键字用来声明不需要保存的状态 private transient int field2; //特殊签名的方法用来定制备忘录生成的具体规则,承担了createMemento()、restoreMemento()的职责 private void writeObject( ObjectOutputStream out ) throws IOException { out.writeInt( field0 ); out.writeUTF( field1 ); } private void readObject( ObjectInputStream in ) throws IOException { field0 = in.readInt(); field1 = in.readUTF(); } } |
而Memento角色则由使用二进制数据流承担,没有对应的Java对象。CareTaker角色由java.io包中一些类承担:
1 2 3 4 5 6 7 8 |
SerializableObject so = new SerializableObject(); ObjectOutputStream output = new ObjectOutputStream( new FileOutputStream( "SerializableObject" ) ); output.writeObject( so ); output.close(); ObjectInputStream input = new ObjectInputStream( new FileInputStream( "SerializableObject" ) ); so = (SerializableObject) input.readObject(); input.close(); |
模式演变
- 增量备忘录:如果原发器的状态很多,而修改的状态很少,可以使用增量方式存储备忘录
- 与原型模式联用:如果原发器的大部分字段都是需要保存的,那么原发器可以实现原型模式,保存备忘录的时候直接保存原发器的克隆
- 离线存储:标准的备忘录模式没有考虑到离线存储的问题,实际上CareTaker可以将备忘录保存为XML、JSON、Java串行化格式,甚至保存到数据库
- 与命令模式联用:命令模式支持撤销,这就涉及到对象状态恢复的问题。可以在执行命令时,将出现变化的状态原值保存到备忘录中,在撤销时,取回备忘录恢复
Leave a Reply