Back to Design Patterns
Behavioral Pattern

Memento Pattern

Capture and externalise an object's internal state so it can be restored later — without violating encapsulation.

Intermediate10 min read

What is the Memento Pattern?

The Memento pattern captures an object's complete internal state into a snapshot object (memento) that can be stored and later used to restore the original object to that exact state — without exposing the object's internals to the outside world.

Think of a video game save point: the game snapshots your character's level, health, position, and inventory. You can die and "load" back to that save. The save file (memento) doesn't expose how the game engine works internally — it just holds enough data to restore the state.

The pattern involves three objects: the Originator (the object being snapshotted), the Memento (the snapshot), and the Caretaker (manages the snapshots — knows when to save and restore, but never looks inside the memento).

Key Idea: Snapshot state into an opaque object. The Caretaker stores it; the Originator creates and consumes it. Encapsulation preserved throughout.

Core Participants

Originator

Originator

The object whose state needs to be saved. It creates mementos (save()) and restores from them (restore()). Only the Originator knows what needs to be captured and how to use it.

Memento

Memento

The snapshot object. Stores the Originator's state. Should be immutable (ideally) — state captured at save time should never change. The Caretaker holds it but cannot read its internals.

Caretaker

Caretaker

Manages when to save and restore. Holds a stack (or list) of mementos. It never inspects or modifies memento contents — it just stores and returns them when needed.

Implementation

// Memento Pattern — Text Editor Undo History

// ─── Memento — stores the snapshot ───────────────────────────
class EditorMemento {
    private final String content;
    private final int    cursorPosition;
    private final String selectedText;

    EditorMemento(String content, int cursor, String selected) {
        this.content       = content;
        this.cursorPosition = cursor;
        this.selectedText  = selected;
    }
    // Only the Originator can access internals
    String getContent()       { return content; }
    int    getCursorPosition(){ return cursorPosition; }
    String getSelectedText()  { return selectedText; }

    @Override
    public String toString() {
        return "Snapshot['" + content.substring(0, Math.min(20, content.length())) + "...']";
    }
}

// ─── Originator — creates and restores mementos ───────────────
class TextEditor {
    private String content       = "";
    private int    cursor        = 0;
    private String selectedText  = "";

    public void type(String text) {
        content = content.substring(0, cursor) + text + content.substring(cursor);
        cursor += text.length();
        System.out.println("[Editor] Content: "" + content + "" | Cursor: " + cursor);
    }

    public void select(int start, int end) {
        selectedText = content.substring(start, end);
        System.out.println("[Editor] Selected: "" + selectedText + """);
    }

    public EditorMemento save() {
        EditorMemento m = new EditorMemento(content, cursor, selectedText);
        System.out.println("[Editor] Saved " + m);
        return m;
    }

    public void restore(EditorMemento memento) {
        content      = memento.getContent();
        cursor       = memento.getCursorPosition();
        selectedText = memento.getSelectedText();
        System.out.println("[Editor] Restored: "" + content + "" | Cursor: " + cursor);
    }
}

// ─── Caretaker — manages history ──────────────────────────────
class History {
    private java.util.Deque<EditorMemento> undoStack = new java.util.ArrayDeque<>();
    private java.util.Deque<EditorMemento> redoStack = new java.util.ArrayDeque<>();

    public void push(EditorMemento m) {
        undoStack.push(m);
        redoStack.clear();
    }

    public EditorMemento undo() {
        if (undoStack.isEmpty()) { System.out.println("[History] Nothing to undo"); return null; }
        EditorMemento m = undoStack.pop();
        redoStack.push(m);
        return undoStack.isEmpty() ? null : undoStack.peek();
    }

    public EditorMemento redo() {
        if (redoStack.isEmpty()) { System.out.println("[History] Nothing to redo"); return null; }
        EditorMemento m = redoStack.pop();
        undoStack.push(m);
        return m;
    }
}

// ─── Client ───────────────────────────────────────────────────
public class App {
    public static void main(String[] args) {
        TextEditor editor  = new TextEditor();
        History    history = new History();

        history.push(editor.save());    // save empty state

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

        editor.type(", World!");
        history.push(editor.save());

        editor.type(" How are you?");
        history.push(editor.save());

        System.out.println("\n--- Undo ---");
        EditorMemento prev = history.undo();
        if (prev != null) editor.restore(prev);

        System.out.println("\n--- Undo again ---");
        prev = history.undo();
        if (prev != null) editor.restore(prev);

        System.out.println("\n--- Redo ---");
        EditorMemento next = history.redo();
        if (next != null) editor.restore(next);
    }
}

When to Use Memento

Undo / Redo Functionality

When users need to reverse actions — text editors, drawing apps, spreadsheets. Each action saves a memento; undo restores the previous one.

TextEditorHistory, CanvasUndoStack

Transaction Rollback

When an operation may fail and needs to be reversed — save state before the operation, restore if it fails.

DatabaseTransaction, BatchProcessor

Game Save Points

When players should be able to save progress and resume later — snapshot the entire game state to a saveable memento.

GameSaveSlot, CheckpointSystem

Wizard / Multi-Step Forms

When a multi-step process needs a "Back" button — save state at each step so the user can navigate back without losing progress.

CheckoutWizard, FormHistory

Pros & Cons

Advantages

  • State capture without violating encapsulation
  • Caretaker never sees internals — clean separation
  • Enables undo/redo and rollback as first-class features
  • Originator remains the only thing that knows its state format
  • Multiple snapshots allow branching history or named save slots

Disadvantages

  • Memory intensive — many snapshots of large objects
  • Deep copying complex objects can be slow
  • Caretaker must manage when snapshots are garbage-collected
  • If Originator changes its state structure, old mementos break

Real-World Examples

Git Commits

Every git commit is a Memento — a complete snapshot of the repository state. git checkout and git revert restore from mementos. The .git directory is the Caretaker.

Java Serialization

Java's Serializable interface lets objects serialise their state to bytes (creating a memento) and deserialise back. Used in session replication, deep cloning, and RMI.

Browser History API

history.pushState() and history.replaceState() save application state mementos. history.back() and history.forward() restore them — the browser manages the caretaker stack.

Redux DevTools Time Travel

Redux DevTools stores every action's resulting state as a memento. Time-travel debugging restores the app to any past state by replaying the sequence of mementos.

What's Next?

Explore related Behavioral patterns: