首页 备忘录模式
文章
取消

备忘录模式

定义

来自 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 就是采用的增量备份的方式。

优化备忘录设计模式

除了上述的缺点,备忘录设计模式本身其实并不完美,通常还可使用下面这些方法进行优化:

  1. 可以使用多个备忘录对象来存储不同时间点的状态,而不仅仅是一个备忘录对象存储当前状态。这样可以更好地支持撤销和重做操作。
  2. 可以对备忘录对象进行压缩和序列化,以便在存储备忘录对象时占用更少的内存,并允许备忘录对象在不同系统之间传输。
  3. 可以使用观察者模式,当原始对象的状态发生更改时,自动通知所有的备忘录对象,以便它们可以更新自己的状态。
  4. 可以使用代理模式来代替备忘录对象的创建和管理,以便可以更好地控制备忘录对象的访问和修改。代理模式还可以实现更高级的功能,例如动态创建和销毁备忘录对象,或者允许只读访问备忘录对象等。
本文由作者按照 CC BY 4.0 进行授权

装饰器模式

适配器模式