备忘录模式,又称快照模式(Snapshot Pattern),是一种行为型设计模式。其思想是:保存一个对象的某个状态,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便在适当的时候恢复对象。
备忘录模式涉及到3个角色:
备忘录角色对任何对象都提供一个宽接口,备忘录角色的内部所存储的状态对所有对象公开。这种模式叫做白箱模式。
具体代码实现:
备忘录:
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
发起人:
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.printf("state of originator now is %s\n", state);
}
// 创建备忘录
public Memento createMemento() {
return new Memento(this.state);
}
// 回滚到备忘录所记录的状态
public void rollbackTo(Memento memento) {
if (memento != null) this.setState(memento.getState());
}
}
负责人:
public class Caretaker {
// 持有一个备忘录引用,并且只维护备忘录,不维护备忘录中的具体内容
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
客户:
public class Client {
private Originator originator = new Originator();
private Caretaker caretaker = new Caretaker();
// 设置一个状态,并记录到备忘录
public void method1() {
originator.setState("STATE 1");
Memento memento = originator.createMemento();
caretaker.setMemento(memento);
}
// 设置一个状态,然后回滚
public void method2() {
originator.setState("STATE 2");
originator.rollbackTo(caretaker.getMemento());
}
}
// 测试
class MementoTest {
public static void main(String[] args) {
Client client = new Client();
client.method1();
client.method2();
}
}
运行结果:
state of originator now is STATE 1
state of originator now is STATE 2
state of originator now is STATE 1
可以看到,白箱模式是破坏封装性的,必须依靠程序员自律才能实现备忘录模式的用意。
备忘录角色对发起人角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做黑箱模式,也叫自述历史模式。
JAVA中同时实现宽接口与窄接口的方法,我们在迭代子模式的文章里提到过了,就是使用私有的成员内部类方式。
具体代码实现:
备忘录、发起人:
// 抽象备忘录,不提供对外的方法,只提供依赖倒置的作用
public interface Memento {
}
// 发起人
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.printf("state of originator now is %s\n", state);
}
// 创建备忘录
public Memento createMemento() {
return new Mem(this.state);
}
// 回滚到备忘录中记录的状态
public void rollbackTo(Memento memento) {
this.setState(((Mem) memento).state);
}
// 以私有成员内部类的方式提供具体备忘录,使备忘录的内容对发起人公开,对其它对象不公开
private class Mem implements Memento {
private String state;
private Mem(String state) {
this.state = state;
}
}
}
负责人、客户和测试的代码和白箱实现是一样的,不再贴出。
运行结果:
state of originator now is STATE 1
state of originator now is STATE 2
state of originator now is STATE 1
上面两个例子都是只存储一个状态的简单实现,常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。
具体代码实现:
// 抽象备忘录
public interface Memento {
}
// 发起人
public class Originator {
private List list = new ArrayList<>(); // 历史状态列表
private int index = 0; // 目录状态指针
// 新增一个状态
public void setState(String state) {
this.list.add(state);
this.index++;
}
// 打印状态列表
public void printStates() {
this.list.forEach(System.out::println);
}
// 创建一个备忘录
public Memento createMemento() {
return new Mem(this.list, this.index);
}
// 回滚到某个备忘录记录的状态
public void rollbackTo(Memento memento) {
Mem mem = (Mem) memento;
this.list = mem.list;
this.index = mem.index;
}
// 黑箱模式实现
private class Mem implements Memento {
private List list;
private int index;
private Mem(List list, int index) {
// 注意这个坑!!像下面这样写,只是把Mem的list指针指向Originator的list指针指向的对象,
// 当对象内容改变,也会体现在Mem的list指针上面,无法做到“记录”的效果。
// 读者可以试试这样写最后结果会有什么不同。
// this.list = list;
this.list = new ArrayList<>(list);
this.index = index;
}
}
}
// 负责人
public class Caretaker {
private Originator originator; // 持有一个发起人的引用
private List list; // 持有一个备忘录列表的引用
public Caretaker(Originator originator) {
this.originator = originator;
this.list = new ArrayList<>();
}
// 新建一个检查点
public void createCheckPoint() {
Memento memento = this.originator.createMemento();
this.list.add(memento);
}
// 回滚到指定的检查点
public void rollbackTo(int index) {
Memento memento = list.get(index);
this.originator.rollbackTo(memento);
}
}
// 客户
public class Client {
private Originator originator = new Originator();
private Caretaker caretaker = new Caretaker(originator);
// 新增若干检查点
public void buildCheckPoints(int count) {
for (int i = 0; i < count; i++) {
String state = "STATE "+i;
originator.setState(state);
caretaker.createCheckPoint();
}
originator.printStates();
}
// 回滚到指定检点
public void rollback(int toIndex) {
caretaker.rollbackTo(toIndex);
originator.printStates();
}
}
// 测试
class MementoTest {
public static void main(String[] args) {
Client client = new Client();
client.buildCheckPoints(5);
System.out.println("==========");
client.rollback(1);
}
}
运行结果:
STATE 0
STATE 1
STATE 2
STATE 3
STATE 4
==========
STATE 0
STATE 1
备忘录模式给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
备忘录模式,尤其是多检查点模式,会产生大量的备忘录对象,是比较消耗资源的。