设计模式之禅——备忘录模式

引: 备忘录模式是我们设计上的“月光宝盒”,可以让我们回到需要的年代;是程序数据的“后悔药”,吃了它就可以返回上一个状态;是设计人员的定心丸,确保即使在最坏的情况下也能获得最近的对象状态。

定义

备忘录模式提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,其定义如下:

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

备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法,其通用类图如下:

memento

下面简单介绍类图中的几个角色:

  1. Originator发起人角色:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  2. Memento备忘录角色:负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  3. Caretaker备忘录管理员角色:对备忘录进行管理,保存和提供备忘录。

下面是它的通用源代码:

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
// 发起人角色
public class Originate {
// 内部状态
private String state = "";

public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
// 创建一个备忘录
public Memento createMemento() {
return new Memento(this.state);
}
// 恢复一个备忘录
public void restoreMemento(Memento _memento) {
this.setState(_memento.getState());
}
}

// 备忘录角色
public class Memento {
// 发起人内部状态
private String state = "";
// 构造函数传递
public Memento(String _state) {
this.state = _state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}

// 备忘录管理者
public class CareTaker {
// 备忘录对象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}

// 场景类
public class Client {
public static void main(String[] args) {
// 定义发起人
Originator originator = new Originator();
// 定义出备忘录管理员
Caretaker caretaker = new Caretaker();
// 创建一个备忘录
caretaker.setMemento(originator.createMemento());
// 恢复一个备忘录
originator.restoreMemento(caretaker.getMemento());
}
}

应用

使用场景

  1. 需要保存和恢复数据的相关状态场景。
  2. 提供一个可回滚的操作。
  3. 需要监控的副本场景。
  4. 数据库连接的事务管理就是用的备忘录模式。

注意事项

  1. 备忘录的生命周期

    备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。

  2. 备忘录性能

    不要在频繁建立备份的场景中使用备忘录模式,原因有而:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。

扩展

clone方式的备忘录

发起人角色融入了发起人角色和备份路角色,利用clone()方法克隆出来的对象充当备忘录对象。但是由于存在深拷贝和浅拷贝的问题,在复杂的场景下它会让你的程序逻辑异常混乱,因此Clone方式的备忘录模式适用于比较简单的场景。

多状态的备忘录模式

将多个状态转换到HashMap中,方便备忘录角色存储。

多备份的备忘录

将备份的数据存入到HashMap中,并给每个不同的备份一个不同的key。

最佳实践

大家主要的还是记住数据库的事务用的是备忘录模式就好了。

参考

  1. 《设计模式之禅》