备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。
备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。
备忘录模式的结构
备忘录模式的结构图如下所示
备忘录模式所涉及的角色有三个:备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色。
备忘录(Memento)角色
备忘录角色又如下责任:
1. 将发起人(Originator)对象的内部状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。
2. 备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
备忘录有两个等效的接口:
窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人(Originator)角色
发起人(Originator)角色有如下责任:
创建一个含有当前的内部状态的备忘录对象。
使用备忘录对象存储其内部状态。
负责人(Caretaker)角色
负责人(Caretaker)角色有如下责任:
负责保存备忘录对象。
不检查备忘录对象的内容。
“白箱”备忘录模式的实现
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。
“白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。
Originator角色
/**
* Created by yangtianrui on 17-3-18.
* 发起人角色
*/
public class Originator {
// 模拟一个状态
private String mState;
public Originator(String state) {
mState = state;
}
public void setState(String state) {
mState = state;
}
public Memento createMemento() {
return new Memento(mState);
}
// 输出状态
public void printState() {
System.out.println(mState);
}
}
Memento角色
/**
* Created by yangtianrui on 17-3-18.
* 备忘录对象,用来存储Originator的状态
*/
public class Memento {
private String mState;
public String getState() {
return mState;
}
public void setState(String state) {
mState = state;
}
public Memento(String state) {
mState = state;
}
}
CareTaker角色
/**
* Created by yangtianrui on 17-3-18.
* 负责人角色,负责人保存对象状态
*/
public class CareTaker {
private Memento mMemento;
public CareTaker(Memento memento) {
mMemento = memento;
}
/**
* 获取状态
*/
public String retrieveState() {
return mMemento.getState();
}
/**
* 存入状态
*/
public void saveState(Memento memento) {
mMemento = memento;
}
}
测试类
public class Main {
public static void main(String[] args) {
// 初始化状态
final Originator originator = new Originator("Initial state.");
originator.printState();
// 使用CareTaker保存状态
final CareTaker careTaker = new CareTaker(originator.createMemento());
// 修改状态
originator.setState("Modify this state.");
// 输出修改后的状态
originator.printState();
// 使用Memento恢复状态
originator.setState(careTaker.retrieveState());
// 输出恢复的状态
originator.printState();
/*结果
Initial state.
Modify this state.
Initial state.
*/
}
}
“黑箱”备忘录模式的实现
在Java语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。
将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;
在外部提供一个标识接口MementoIF给Caretaker以及其他对象。
这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。
宽窄接口的设计
IMemento
对外仅提供简单的接口
/**
* Created by yangtianrui on 17-3-18.
* 对外提供的备忘录接口
*/
public interface IMemento {
}
Originator角色,通过内部类,对备忘录进行封装
public class Originator {
// 模拟保存的状态
private String mState;
public Originator(String state) {
mState = state;
}
public void setState(String state) {
mState = state;
}
public void printState() {
System.out.println(mState);
}
// 创建备忘录保存对象
public IMemento createMemento() {
// 保存对象
return new OriginMemento(mState);
}
// 从备忘录中恢复状态
public void restoreMemento(IMemento memento) {
if (memento instanceof OriginMemento) {
mState = ((OriginMemento) memento).getState();
}
}
/**
* 通过内部类,将对象状态进行封装
*/
private class OriginMemento implements IMemento {
private String mState;
public OriginMemento(String state) {
mState = state;
}
public String getState() {
return mState;
}
public void setState(String state) {
mState = state;
}
}
}
CareTaker角色,对备忘录进行管理
public class CareTaker {
// 对外部只是表现为接口,体现了封装性
private IMemento mMemento;
public void setMemento(IMemento memento) {
mMemento = memento;
}
public IMemento getMemento() {
return mMemento;
}
}
测试类
public class Main {
public static void main(String[] args) {
// 初始状态
final Originator originator = new Originator("Initial state.");
originator.printState();
// 存储状态到备忘录
final CareTaker careTaker = new CareTaker();
careTaker.setMemento(originator.createMemento());
// 更改状态
originator.setState("Modified state.");
originator.printState();
// 恢复状态
originator.restoreMemento(careTaker.getMemento());
originator.printState();
/*
结果
*Initial state.
*Modified state.
*Initial state.
*/
}
}