这篇博客我们来介绍一下备忘录模式(Memento Pattern),也是行为型模式设计模式之一,备忘录模式又称为快照(Snapshot Pattern)模式或者 Token 模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问(an opaque object),目的是为了保护被保存的这些对象状态的完整性以及内部实现不向外暴露。
设计模式总目录
特点
在不破坏封装的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。
备忘录模式使用的场景:
- 需要保存一个对象在某一个时刻的状态或部分状态;
- 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以简洁访问其内部状态。
UML类图
来看看备忘录模式的 uml 类图:
备忘录模式有三个角色:
- Originator:负责创建一个备忘录,可以记录、恢复自身的内部状态,同时 Originator 还可以根据需要决定 Memento 存储自身的哪些内部状态。
- Memento:备忘录角色,用于存储 Originator 的内部状态,并且可以防止 Originator 以外的对象访问 Memento。
- CareTaker:负责存储备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
我们这里可以写出通用代码:
Originator.class
public class Originator {
private int state = 0;
public void setState(int state) {
this.state = state;
}
public void print() {
System.out.print("state = " + state + "\n");
}
public void restore(Memento memento) {
setState(memento.getState());
}
public Memento createMemoto() {
Memento memento = new Memento();
memento.setState(state);
return memento;
}
public static void main(String args[]) {
Originator originator = new Originator();
originator.setState(1);
Caretaker caretaker = new Caretaker();
caretaker.storeMemento(originator.createMemoto());
System.out.print("before\n");
originator.print();
originator.setState(2);
System.out.print("after\n");
originator.print();
originator.restore(caretaker.restoreMemento());
System.out.print("restore to the original state\n");
originator.print();
}
}
Memento.class
public class Memento {
private int mState = 0;
public void setState(int state) {
this.mState = state;
}
public int getState() {
return mState;
}
}
Caretaker.class
public class Caretaker {
private Memento memento;
public Memento restoreMemento() {
return memento;
}
public void storeMemento(Memento memento) {
this.memento = memento;
}
}
最后输出:
before
state = 1
after
state = 2
restore to the original state
state = 1
上面的实现方式中备忘录角色的内部所存储的状态对所有对象公开,因此很多时候这个被称为“白箱”备忘录模式,它将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的,但是通过程序员自律,同样可以一定程度上实现模式的大部分用意,所以它仍然是有意义的;“黑箱”备忘录模式和白箱备忘录模式的不同之处是 Memento 设计成了 Originator 的内部类,从而将 Memento 的对象封装在 Originator 里面,在外面提供一个标识接口 MementoIF 给 Caretaker 以及其他对象,并且让 Memento 实现 MementoIF 接口,或者不用 MementoIF 接口,直接将 Memento 类设计成静态内部类也是可以的。
示例与源码
在 Android 源码中,我们经常会用到的 onSaveInstanceState 和 onRestoreInstanceState 就是一个备忘录模式,在这个过程中,Activity 扮演了Caretaker 的角色,负责存储、恢复 UI 的状态信息;Activity、Fragment、View、ViewGroup 等对象为 Originator 角色,也就是需要存储状态的对象;Memento 则是由 Bundle 类扮演。Activity 在停止之前会根据 Activity 的退出情景来选择是否需要存储状态,在重新启动该 Activity 时会判断 ActivityClientRecord 对象中是否存储了 Activity 的状态,如果含有状态则调用 Activity 的 onRestoreInstanceState 函数,从而使得 Activity 的 UI 效果和上次保持一致,这样一来,就保证了在非正常情况退出 Activity 时不会丢失数据的情况,很好的提升了用户体验。
我们这里以 wiki 上的”黑箱”备忘录模式为例,看一下和”白箱”备忘录模式的区别:
Originator.class
class Originator {
private String state;
// The class could also contain additional data that is not part of the
// state saved in the memento..
public void set(String state) {
System.out.println("Originator: Setting state to " + state);
this.state = state;
}
public Memento saveToMemento() {
System.out.println("Originator: Saving to Memento.");
return new Memento(this.state);
}
public void restoreFromMemento(Memento memento) {
this.state = memento.getSavedState();
System.out.println("Originator: State after restoring from Memento: " + state);
}
public static class Memento {
private final String state;
public Memento(String stateToSave) {
state = stateToSave;
}
private String getSavedState() {
return state;
}
}
}
Caretaker.class
public static void main(String[] args) {
List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
Originator originator = new Originator();
originator.set("State1");
originator.set("State2");
savedStates.add(originator.saveToMemento());
originator.set("State3");
// We can request multiple mementos, and choose which one to roll back to.
savedStates.add(originator.saveToMemento());
originator.set("State4");
originator.restoreFromMemento(savedStates.get(1));
}
输出:
Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3
将 Memento 类设置成为 Originator 的静态内部类之后,Memento 的内部实现细节就可以只对 Originator 暴露了,实现了”黑箱”的封装性,我们这里省掉了 Caretaker 和 MementoIF 这两个角色,根据需要也可以去实现这两个角色。
总结
备忘录模式是在不破坏封装的条件下,通过备忘录对象(Memento)存储另一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;
- 实现了信息的封装,维护内聚,使得用户不需要关心状态的保存细节。
缺点:消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,恢复状态的过程也可能会很耗时。
源码下载
https://github.com/Jakey-jp/Design-Patterns/tree/master/MementoPattern
引用
https://en.wikipedia.org/wiki/Memento_pattern
http://www.cnblogs.com/java-my-life/archive/2012/06/06/2534942.html