Design Pattern

Memento Pattern

Clean Java-only production-ready implementation.


You need to save and restore an object's state without exposing its internal structure. Memento captures the state in an opaque snapshot, the originator creates/restores from mementos, and the caretaker manages the history.

// ─── EXAMPLE 1 ──────────────────────────────────────────────────────────────
// WHAT WE ARE IMPLEMENTING:
// A graphical canvas history buffer saving intermediate drafts to enable
// instant Ctrl-Z restores.
//
// WHERE THE MEMENTO FITS IN:
// CanvasState acts as the Memento storing values. CanvasEditor is the
// Originator whose internal state changes. History represents the Caretaker
// tracking history.
// ────────────────────────────────────────────────────────────────────────────
import java.util.ArrayDeque;
import java.util.Deque;

// --- Memento (opaque snapshot) ---
class EditorMemento {
    private final String content;
    private final int cursorPosition;

    EditorMemento(String content, int cursorPosition) {
        this.content = content;
        this.cursorPosition = cursorPosition;
    }

    String getContent() { return content; }
    int getCursorPosition() { return cursorPosition; }
}

// --- Originator ---
class TextEditor {
    private String content = "";
    private int cursorPosition = 0;

    public void write(String text) {
        content += text;
        cursorPosition = content.length();
    }

    public void deleteLast(int chars) {
        if (content.length() >= chars) {
            content = content.substring(0, content.length() - chars);
            cursorPosition = content.length();
        }
    }

    // Save state to memento
    public EditorMemento save() {
        return new EditorMemento(content, cursorPosition);
    }

    // Restore state from memento
    public void restore(EditorMemento memento) {
        this.content = memento.getContent();
        this.cursorPosition = memento.getCursorPosition();
    }

    public String getContent() { return content; }
}

// --- Caretaker ---
class EditorHistory {
    private final Deque<EditorMemento> history = new ArrayDeque<>();

    public void push(EditorMemento memento) {
        history.push(memento);
    }

    public EditorMemento pop() {
        return history.isEmpty() ? null : history.pop();
    }
}

public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        EditorHistory history = new EditorHistory();

        editor.write("Hello ");
        history.push(editor.save());

        editor.write("World!");
        history.push(editor.save());

        editor.write(" This is extra.");
        System.out.println("Current: " + editor.getContent());

        // Undo twice
        EditorMemento restored = history.pop();
        if (restored != null) editor.restore(restored);
        System.out.println("Undo 1: " + editor.getContent());

        restored = history.pop();
        if (restored != null) editor.restore(restored);
        System.out.println("Undo 2: " + editor.getContent());
    }
}