Design Pattern

Memento Pattern

Clean Java-only production-ready implementation.


What Problem It Solves

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.

Implementation

// โ”€โ”€โ”€ 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());
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...