1. 概要

このチュートリアルでは、Mementoデザインパターンとは何か、およびその使用方法を学習します。

まず、少し理論を説明します。 次に、パターンの使用法を説明する例を作成します。

2. Mementoデザインパターンとは何ですか?

Gang of Fourの本で説明されているMementoデザインパターンは、ビヘイビアーデザインパターンです。 Mementoデザインパターンは、元に戻せないアクションを実装するためのソリューションを提供します。 これを行うには、特定の瞬間のオブジェクトの状態を保存し、実行したアクションを元に戻す必要がある場合はそれを復元します。

実際には、状態を保存する必要があるオブジェクトはオリジネーターと呼ばれます。 Caretakerは、状態の保存と復元をトリガーするオブジェクトであり、Mementoと呼ばれます。

Mementoオブジェクトは、世話人にできるだけ少ない情報を公開する必要があります。 これは、カプセル化の原則に違反するため、オリジネーターの内部状態を外部に公開しないようにするためです。 ただし、発信者は、元の状態に復元するために十分な情報にアクセスする必要があります。

さまざまなオブジェクトが互いにどのように相互作用するかを示す簡単なクラス図を見てみましょう。

ご覧のとおり、オリジネーターはMementoを作成して消費できます。 その間、世話人はそれを復元する前に状態を保持するだけです。 オリジネーターの内部表現は、外部世界から隠されています。

ここでは、オリジネーターの状態を表すために単一のフィールドを使用しましたが、 1つのフィールドに限定されず、必要な数のフィールドを使用できた可能性があります。 さらに、Mementoオブジェクトに保持されている状態は、Originatorの完全な状態と一致する必要はありません。 保持された情報がオリジネーターの状態を復元するのに十分である限り、私たちは行ってもいいです。

3. Mementoデザインパターンを使用する場合

通常、Mementoデザインパターンは、一部のアクションを元に戻すことができない状況で使用されるため、前の状態にロールバックする必要があります。 ただし、オリジネーターの状態が重い場合、Mementoデザインパターンを使用すると、作成プロセスにコストがかかり、メモリの使用量が増える可能性があります。

4. Mementoパターンの例

4.1. 初期サンプル

Mementoデザインパターンの例を見てみましょう。 テキストエディタがあると想像してみましょう。

public class TextEditor {

    private TextWindow textWindow;

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

現在入力されているテキストを保持するテキストウィンドウがあり、テキストを追加する方法を提供します。

public class TextWindow {

    private StringBuilder currentText;

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

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

4.2. Memento

ここで、テキストエディタにいくつかの保存および元に戻す機能を実装してほしいと想像してみましょう。 保存するときは、現在のテキストを保存する必要があります。 したがって、その後の変更を元に戻すと、保存したテキストが復元されます。

そのために、Mementoデザインパターンを利用します。 まず、ウィンドウの現在のテキストを保持するオブジェクトを作成します。

public class TextWindowState {

    private String text;

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

    public String getText() {
        return text;
    }
}

このオブジェクトは私たちの記念品です。 ご覧のとおり、部外者による現在のテキストの更新を防ぐために、StringBuilderの代わりにStringを使用することを選択しています。

4.3. オリジネーター

その後、 TextWindow クラスにMementoオブジェクトを作成および使用するためのメソッドを提供して、TextWindowをオリジネーターにする必要があります。

private StringBuilder currentText;

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

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

save()メソッドを使用するとオブジェクトを作成でき、 restore()メソッドはそれを使用して前の状態を復元します。

4.4. 世話人

最後に、TextEditorクラスを更新する必要があります。 世話人として、オリジネーターの状態を保持し、必要に応じて復元するように要求します:

private TextWindowState savedTextWindow;

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

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

4.5. ソリューションのテスト

サンプル実行で機能するかどうかを見てみましょう。 エディターにテキストを追加して保存し、さらに追加して、最後に元に戻すと想像してみてください。 これを実現するために、現在のテキストの Stringを返すprint()メソッドをTextEditorに追加します。

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");

ご覧のとおり、Mementoは追加する前に保存されているため、最後の文は現在のテキストの一部ではありません。

5. 結論

この短い記事では、Mementoデザインパターンとその使用目的について説明しました。 また、簡単なテキストエディタでの使用法を示す例も示しました。

この記事で使用されている完全なコードは、GitHubにあります。