备忘录(Memento)模式
备忘录模式(Memento Pattern)又叫快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。
意图(Intent):
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先的保存的状态。
备忘录(Memento)对象是一个用来存储另外一个对象内部状态的快照(Snapshot)的对象。备忘录模式(Memento)的用意是在不破坏封装(encapsulation)的条件下,将一个对象的状态捕获(Capture)住,并外部化(Externalize),存储起来,从而可以在将来合适的时候吧这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。
常见的软件系统往往不止存储一个状态,而是需要存储多个状态。这些状态常常是一个对象历史发展的不同阶段的快照,存储这些快照的备忘录对象叫做对象的历史;某一个快照所处的位置叫做检查点(Check Point)。
备忘录模式的暗箱实现:
// 标示接口
interface MementoIF {
}
class Caretaker {
private MementoIF memento;
public MementoIF getMemento() {
return memento;
}
public void setMemento(MementoIF memento) {
this.memento = memento;
}
}
class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Originator(String state) {
this.state = state;
}
/**
* 工厂方法,返还一个新的备忘录对象
*/
public MementoIF createMemento() {
return new Memento(this.state);
}
public void restoreMemento(MementoIF memento) {
Memento m = (Memento)memento;
this.setState(m.getSavedState());
}
private class Memento implements MementoIF {
private String savedState;
public Memento(String somestate) {
savedState = somestate;
}
public String getSavedState() {
return savedState;
}
public void setSavedState(String savedState) {
this.savedState = savedState;
}
}
}
public class Client {
private static Originator o = new Originator("");
private static Caretaker c = new Caretaker();
public static void main(String[] args) {
// 改变负责人的状态
o.setState("on");
// 创建备忘录对象,并将发起人对象的状态存储起来
c.setMemento(o.createMemento());
System.out.println(o.getState());
// 修改负责人的状态
o.setState("off");
System.out.println(o.getState());
o.restoreMemento(c.getMemento());
System.out.println(o.getState());
}
}
代码分析:
Memento设计成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口MementoIF给Caretaker以及其它对象。这样,Originator类看到的是Memento的所有接口,而Caretaker以及其他对象看到的的仅仅是标示接口MementoIF所暴露出来的接口。
备忘录模式的白箱实现:
class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Originator(String state) {
this.state = state;
}
/**
* 工厂方法,返还一个新的备忘录对象
*/
public Memento createMemento() {
return new Memento(this.state);
}
public void restoreMemento(Memento memento) {
Memento m = (Memento)memento;
this.setState(m.getSavedState());
}
}
class Memento {
private String savedState;
public Memento(String somestate) {
savedState = somestate;
}
public String getSavedState() {
return savedState;
}
public void setSavedState(String savedState) {
this.savedState = savedState;
}
}
class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class Client {
private static Originator o = new Originator("");
private static Caretaker c = new Caretaker();
public static void main(String[] args) {
// 改变负责人的状态
o.setState("on");
// 创建备忘录对象,并将发起人对象的状态存储起来
c.setMemento(o.createMemento());
System.out.println(o.getState());
// 修改负责人的状态
o.setState("off");
System.out.println(o.getState());
o.restoreMemento(c.getMemento());
System.out.println(o.getState());
}
}
备忘录模式的分析:
备忘录模式所涉及的角色有三个:备忘录(Memento)角色、发起人(Originator)角色和负责人(Caretaker)角色。
备忘录(Memento)角色:
将发起人(Orginator)对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人Originator对象的内部状态。
备忘录可以保护其内容部被发起人(Orginator)对象之外的任何对象读取。备忘录有两个等效接口:
窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象;
宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人(Originator)角色:
创建一个含有当前的内部状态的备忘录对象。
使用备忘录对象存储其内部状态。
负责人(Caretaker)角色
负责人角色有如下的责任:
负责人保存备忘录对象。
不检查备忘录对象的内容。
双重接口以其在Java语言中的实现:
宽接口和窄接口:在前面对备忘录模式的描述里,“不破坏封装”是一个关键的修饰词。一个系统可能需要将某个对象的状态,比如一个可视构件对象的位置、颜色、大小等状态参数全部或部分暴露给外界,这就是说,如果此对象提供一些公开接口以提供其内部状态的话,会产生外部代码直接利用此接口修改此对象的潜在危险。
备忘录模式利用宽接口(wide interface)和窄接口(narrow interface)的设计解决了这个问题。
所谓的双重接口,就是对某一个对象提供宽接口,而对另外一些对象提供窄接口。
备忘录模式和多重检查点:
前面所给出的白箱和黑箱的示意性的实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的软件系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做多个检查点。