Memento Design Pattern in Java – Java中的Memento设计模式

最后修改: 2019年 8月 20日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn what the Memento Design Pattern is and how to use it.

在本教程中,我们将学习什么是Memento设计模式以及如何使用它。

First, we’ll go through a bit of theory. Then, we’ll create an example where we’ll illustrate the usage of the pattern.

首先,我们将通过一些理论。然后,我们将创建一个例子,说明该模式的用法。

2. What Is the Memento Design Pattern?

2.什么是Memento设计模式?

The Memento Design Pattern, described by the Gang of Four in their book, is a behavioral design pattern. The Memento Design Pattern offers a solution to implement undoable actions. We can do this by saving the state of an object at a given instant and restoring it if the actions performed since need to be undone.

Gang of Four在他们的书中描述的Memento设计模式,是一种行为设计模式。Memento设计模式为实现可撤销的操作提供了一个解决方案。我们可以通过保存对象在某一时刻的状态,并在此后执行的操作需要撤销时恢复该状态来实现这一目的。

Practically, the object whose state needs to be saved is called an Originator. The Caretaker is the object triggering the save and restore of the state, which is called the Memento.

实际上,其状态需要被保存的对象被称为发起者。看管者是触发保存和恢复状态的对象,它被称为记忆者。

The Memento object should expose as little information as possible to the Caretaker. This is to ensure that we don’t expose the internal state of the Originator to the outside world, as it would break encapsulation principles. However, the Originator should access enough information in order to restore to the original state.

Memento对象应该尽可能少地向看守者暴露信息。这是为了确保我们不向外界暴露起源者的内部状态,因为这将破坏封装原则。然而,起源者应该访问足够的信息,以便恢复到原始状态。

Let’s see a quick class diagram illustrating how the different objects interact with each other:

让我们看看一个快速的类图,说明不同的对象是如何相互作用的。

Memento Design Pattern 1

Memento Design Pattern 1

As we can see, the Originator can produce and consume a Memento. Meanwhile, the Caretaker only keeps the state before restoring it. The internal representation of the Originator is kept hidden from the external world.

正如我们所看到的,起源者可以生产和消费一个纪念品。同时,看守者只保留恢复前的状态。起源者的内部表征对外部世界是隐藏的。

Here, we used a single field to represent the state of the Originator, though we’re not limited to one field and could have used as many fields as necessary. Plus, the state held in the Memento object doesn’t have to match the full state of the Originator. As long as the kept information is sufficient to restore the state of the Originator, we’re good to go.

在这里,我们用一个字段来表示起源者的状态,尽管我们不限于一个字段,可以根据需要使用许多字段。另外,保存在Memento对象中的状态不一定要与发起者的全部状态相匹配。只要保留的信息足以恢复起源者的状态,我们就可以了。

3. When to Use Memento Design Pattern?

3.何时使用Memento设计模式?

Typically, the Memento Design Pattern will be used in situations where some actions are undoable, therefore requiring to rollback to a previous state. However, if the state of the Originator is heavy, using the Memento Design Pattern can lead to an expensive creation process and increased use of memory.

通常情况下,Memento设计模式将被用于某些行动可以撤销的情况下,因此需要回滚到之前的状态。然而,如果发起者的状态很重,使用Memento设计模式会导致昂贵的创建过程,并增加对内存的使用。

4. Example of the Memento Pattern

4.Memento模式的例子

4.1. Initial Sample

4.1.初始样品

Let’s now see an example of the Memento Design Pattern. Let’s imagine we have a text editor:

现在让我们看看Memento设计模式的一个例子。让我们想象一下,我们有一个文本编辑器。

public class TextEditor {

    private TextWindow textWindow;

    public TextEditor(TextWindow textWindow) {
        this.textWindow = textWindow;
    }
}

It has a text window, which holds the currently entered text, and provides a way to add more text:

它有一个文本窗口,保存着当前输入的文本,并提供了一个添加更多文本的方法。

public class TextWindow {

    private StringBuilder currentText;

    public TextWindow() {
        this.currentText = new StringBuilder();
    }

    public void addText(String text) {
        currentText.append(text);
    }
}

4.2. Memento

4.2 纪念品

Now, let’s imagine we want our text editor to implement some save and undo features. When saving, we want our current text to be saved. Thus, when undoing subsequent changes, we’ll have our saved text restored.

现在,让我们设想一下,我们希望我们的文本编辑器实现一些保存和撤销的功能。当保存时,我们希望我们的当前文本被保存。因此,当撤销后续修改时,我们将恢复我们保存的文本。

In order to do that, we’ll make use of the Memento Design Pattern. First, we’ll create an object holding the current text of the window:

为了做到这一点,我们将利用Memento设计模式。首先,我们将创建一个对象,保存窗口的当前文本。

public class TextWindowState {

    private String text;

    public TextWindowState(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

This object is our Memento. As we can see, we choose to use String instead of StringBuilder to prevent any update of the current text by outsiders.

这个对象是我们的Memento。我们可以看到,我们选择使用String而不是StringBuilder,以防止外人对当前文本的任何更新。

4.3. Originator

4.3.发起人

After that, we’ll have to provide the TextWindow class with methods to create and consume the Memento object, making the TextWindow our Originator:

之后,我们必须为TextWindow类提供创建和消费Memento对象的方法,使TextWindow成为我们的发起者:

private StringBuilder currentText;

public TextWindowState save() {
    return new TextWindowState(currentText.toString());
}

public void restore(TextWindowState save) {
    currentText = new StringBuilder(save.getText());
}

The save() method allows us to create the object, while the restore() method consumes it to restore the previous state.

save()方法允许我们创建对象,而restore()方法消耗它以恢复之前的状态。

4.4. Caretaker

4.4.看守所

Finally, we have to update our TextEditor class. As the Caretaker, it will hold the state of the Originator and ask to restore it when needed:

最后,我们必须更新我们的TextEditor类。作为看管者,它将持有发起者的状态,并在需要时要求恢复它:

private TextWindowState savedTextWindow;

public void hitSave() {
    savedTextWindow = textWindow.save();
}

public void hitUndo() {
    textWindow.restore(savedTextWindow);
}

4.5. Testing the Solution

4.5.测试解决方案

Let’s see if it works through a sample run. Imagine we add some text to our editor, save it, then add some more and, finally, undo. In order to achieve that, we’ll add a print() method on our TextEditor that returns a String of the current text:

让我们通过一个示例运行看看它是否有效。想象一下,我们向我们的编辑器添加一些文本,保存它,然后再添加一些,最后,撤销。为了实现这一点,我们将在我们的TextEditor上添加一个print()方法,返回当前文本的String

TextEditor textEditor = new TextEditor(new TextWindow());
textEditor.write("The Memento Design Pattern\n");
textEditor.write("How to implement it in Java?\n");
textEditor.hitSave();
 
textEditor.write("Buy milk and eggs before coming home\n");
 
textEditor.hitUndo();

assertThat(textEditor.print()).isEqualTo("The Memento Design Pattern\nHow to implement it in Java?\n");

As we can see, the last sentence is not part of the current text, as the Memento was saved before adding it.

我们可以看到,最后一句话不是当前文本的一部分,因为Memento在添加它之前被保存了。

5. Conclusion

5.总结

In this short article, we explained the Memento Design Pattern and what it can be used for. We also went through an example illustrating its usage in a simple text editor.

在这篇短文中,我们解释了Memento设计模式以及它可以用来做什么。我们还通过一个例子说明了它在一个简单的文本编辑器中的应用。

The full code used in this article can be found over on GitHub.

本文中使用的完整代码可以在GitHub上找到over