初探备忘录模式

代码链接

koonchen/design-patterns/memento

要点

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。

有时有必要记录一个对象的内部状态,但是对象通常封装了其部分或所有的状态信息,使得其状态不能被其他对象访问,也就就不可能在该对象之外保存其状态;而暴露内部状态又将违反封装的原则。

备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态;而另一者被称为备忘录的原发器,当需要设置原发器的检查点时,取消操作机制会向原发器请求一个备忘录。

原发器用描述当前状态的信息来初始化该备忘录,只有原发器可以向备忘录中存取信息,备忘录对其他的对象是不可见的。

ConstraintSolver 作为一个原发器,发生以下的事件序列:

  1. 作为移动操作的一个副作用,编辑器向 ConstraintSolver 请求一个备忘录。
  2. ConstraintSolver 创建并返回一个备忘录,在这个例子中该备忘录是 SolverState 类的一个实例。
  3. 此后,当用户取消移动操作,编辑器将 SolverState 备忘录送回给 ConstraintSolver
  4. 根据 SolverState 备忘录的信息,ConstraintSolver 改变它的内部结构以精确地讲它的等式和变量返回给它们各自当前的状态。

备忘录模式的适用性:

  • 必须保存一个对象在某个时刻的状态,这样以后需要时它才能恢复到指定的状态。
  • 如果一个用接口让其他对象直接得到这些状态,将会暴露对象的细节并破坏对象的封装。

备忘录模式的参与者:

  • Memento — 备忘录存储原发器对象的内部结构。
  • Originator — 原发器创建一个备忘录,用以记录当前时刻它的内部状态。
  • Caretaker — 负责保存好备忘录。

备忘录模式的结构:

cover

备忘录模式有以下效果:

  • 保持封装边界:使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。
  • 它简化了原发器:在其他的保持封装性的设计中,原发器负责保持客户请求过的内部状态版本。
  • 使用备忘录可能代价很高:如果原发器在生成备忘录时必须存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。
  • 定义窄接口和宽接口:在一些语言中可能难以保证只有原发器可访问备忘录的状态。
  • 维护备忘录潜在的代价:管理器负责删除它维护的备忘录。

实现

Java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.ArrayList;
import java.util.List;

class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return this.state;
}
}

class Originator {
private String state = "before";
public void setMemento(Memento memento) {
state = memento.getState();
}
public Memento createMemento() {
return new Memento(state);
}
}

class Caretaker {
private List<Memento> list;
public Caretaker() {
this.list = new ArrayList<>();
}
public void add(Memento memento) {
list.add(memento);
}
public List<Memento> getList() {
return this.list;
}
}

public class MementoClient {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
caretaker.add(originator.createMemento());
List<Memento> list = caretaker.getList();
System.out.println(list.get(0).getState());
}
}

相关模式:

Command : 命令模式可以使用备忘录来为可撤销的操作维护状态。

Iterator : 如先前所述备忘录可用于迭代。