定义
来自 GOF 的定义:Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.
翻译过来就是:在不违反封装原则的前提下,捕获并外部化一个对象的内部状态,以便将来可以恢复到这个状态。
从定义来看还是非常容易理解的,即将对象在内存某一时刻的状态给它拷贝一份保留下来,以便后续随时恢复到这个状态。
另外是它的前提,不违反封装原则,即:原则意义上复制品或者说快照,不允许被其他对象修改,只能通过原对象进行设置/恢复。显然如果违反这个原则,该模式就没法有效的起到它的作用,比如快照可能被其他对象修改/被其他对象使用。
演示示例
下面是一个非常通俗的演示示例:
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 定义备忘录类
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 定义原始类
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 定义负责人类行为
class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
// 测试类
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: " + originator.getState());
}
}
这段代码演示了备忘录设计模式的实现,该模式的主要目的是在不破坏封装性的情况下,允许在对象之间保存和恢复状态。
在这个例子中,有三个类:Originator(原始类)、Memento(备忘录类)和CareTaker(负责人类)。
Originator类维护了一个状态属性state,并提供了saveStateToMemento()和getStateFromMemento()方法来创建备忘录对象和从备忘录对象中恢复状态。saveStateToMemento()方法创建并返回一个Memento对象,该对象封装了当前的状态属性值,而getStateFromMemento()方法接收一个Memento对象,用于从中获取状态属性值并还原到原始类的state属性中。
Memento类定义了一个名为state的私有属性,并提供了一个构造函数和一个getState()方法,构造函数用于初始化state属性,getState()方法用于返回当前的状态属性值。
CareTaker类维护了一个Memento对象列表,提供了add()和get()方法来向列表中添加备忘录对象以及从列表中获取备忘录对象。在这个例子中,CareTaker类用于存储Originator对象的不同状态快照。
在main()方法中,创建了一个Originator对象和一个CareTaker对象,并对Originator对象的状态属性进行多次修改,每次修改后调用saveStateToMemento()方法创建备忘录对象并存储到CareTaker对象中。在最后,通过调用getStateFromMemento()方法,从不同的备忘录对象中恢复Originator对象的状态,并输出当前状态的值。
该设计模式的优点是可以使得对象的状态恢复到之前的某个状态,而且不会破坏其封装性和整体的内聚性。缺点是如果需要保存的状态数据量很大,将会占用较大的内存空间。
如何优化内存和时间消耗?
上面的示例缺点很明显:如果要 备份的对象数据比较大,备份频率又比较高,那快照占用的内存会比较大,备份和恢复的耗 时会比较长。这个问题该如何解决呢?
有一个通用的解决方案:“低频率全量备份”和“高频率增量备份”相结合的方法。全量备份即快照,所谓“增量备份”,指的是记录每次操作或数据变动。事实上 MySQL 的 redo log 和 binlog 就是采用的增量备份的方式。
优化备忘录设计模式
除了上述的缺点,备忘录设计模式本身其实并不完美,通常还可使用下面这些方法进行优化:
- 可以使用多个备忘录对象来存储不同时间点的状态,而不仅仅是一个备忘录对象存储当前状态。这样可以更好地支持撤销和重做操作。
- 可以对备忘录对象进行压缩和序列化,以便在存储备忘录对象时占用更少的内存,并允许备忘录对象在不同系统之间传输。
- 可以使用观察者模式,当原始对象的状态发生更改时,自动通知所有的备忘录对象,以便它们可以更新自己的状态。
- 可以使用代理模式来代替备忘录对象的创建和管理,以便可以更好地控制备忘录对象的访问和修改。代理模式还可以实现更高级的功能,例如动态创建和销毁备忘录对象,或者允许只读访问备忘录对象等。