The Memento pattern captures and externalizes an object’s internal state so that the object can be restored to this state later, without violating encapsulation. It enables undo, redo, and snapshot-based recovery.

Intent and Motivation

Intent: Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.

Motivation: A text editor needs undo: the document must save its state before each edit and restore it on Ctrl+Z. Exposing all private fields via public getters breaks encapsulation. Memento solves this with three roles: the Originator (the object being saved) creates a Memento containing a snapshot of its state; the Caretaker stores mementos but cannot inspect or modify their contents. Only the Originator can read a memento to restore state.

This pattern is the foundation of undo stacks, game save/load, and database transaction rollback points.

Structure (UML-like)

  ┌──────────────┐  createMemento()  ┌──────────────┐
│  Originator  │ ────────────────► │   Memento    │
├──────────────┤                   ├──────────────┤
│ - state      │  setMemento()     │ - state      │ (opaque to caretaker)
│ + createMem. │ ◄──────────────── │ (snapshot)   │
│ + setMemento │                   └──────▲───────┘
└──────────────┘                          │ stores
                                          │
                                   ┌──────┴───────┐
                                   │  Caretaker   │
                                   ├──────────────┤
                                   │ - mementos[] │
                                   │ + save(m)    │
                                   │ + restore()  │
                                   └──────────────┘
  

Participants:

  • Memento — stores a snapshot of Originator state; opaque to all except Originator.
  • Originator — creates mementos from its state; restores state from mementos.
  • Caretaker — stores mementos; never inspects or modifies their contents.

Java Example

  import java.util.*;

// Memento
class EditorMemento {
    private final String content;

    EditorMemento(String content) { this.content = content; }
    String getContent() { return content; } // package-private or inner class
}

// Originator
class Editor {
    private String content = "";

    void write(String text) { content += text; }
    String getContent() { return content; }

    EditorMemento save() {
        return new EditorMemento(content);
    }

    void restore(EditorMemento memento) {
        this.content = memento.getContent();
    }
}

// Caretaker
class History {
    private final Stack<EditorMemento> mementos = new Stack<>();

    void push(EditorMemento m) { mementos.push(m); }

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

// Usage
Editor editor = new Editor();
History history = new History();

editor.write("Hello ");
history.push(editor.save());
editor.write("World");
System.out.println(editor.getContent()); // Hello World

editor.restore(history.pop());
System.out.println(editor.getContent()); // Hello 
  

For strict encapsulation, make Memento a private inner class of Originator.

JavaScript Example

  // Originator
class GameCharacter {
  constructor() {
    this.level = 1;
    this.health = 100;
    this.inventory = [];
  }

  createMemento() {
    return {
      level: this.level,
      health: this.health,
      inventory: [...this.inventory] // deep copy
    };
  }

  restore(memento) {
    this.level = memento.level;
    this.health = memento.health;
    this.inventory = [...memento.inventory];
  }

  levelUp() { this.level++; this.health += 20; }
  takeDamage(amount) { this.health -= amount; }
}

// Caretaker
class SaveManager {
  constructor() { this.saves = []; }

  save(character) {
    this.saves.push(character.createMemento());
  }

  load(character, index) {
    character.restore(this.saves[index]);
  }
}

const hero = new GameCharacter();
const saves = new SaveManager();
saves.save(hero);
hero.levelUp();
hero.takeDamage(30);
console.log(hero); // level 2, health 90
saves.load(hero, 0);
console.log(hero); // level 1, health 100
  

Real-World Use Cases

Framework / System Usage
Text editor undo Each keystroke snapshot stored as memento on undo stack.
Git commits Each commit is a memento (snapshot) of the repository state.
Database savepoints SAVEPOINT creates a rollback memento within a transaction.
Game save/load Player state serialized to memento files for checkpoint recovery.
VM snapshots Hypervisors capture full machine state for rollback.
Java serialization ObjectOutputStream creates mementos for object graphs.

Pros and Cons

Pros Cons
Preserves encapsulation — state saved without exposing internals Caretaker storing many mementos consumes significant memory
Simplifies Originator — state management delegated to memento/caretaker Memento interface may be wide if many state fields exist
Easy to implement undo/redo with a stack of mementos Deep copying state can be expensive for large objects
Snapshots enable time-travel debugging and audit trails Caretaker must manage memento lifecycle (prune old snapshots)
Works with Command pattern for full undo/redo systems Serialization-based mementos may break on class structure changes

When to Use vs When NOT to Use

Use when:

  • You need to save and restore object state (undo, checkpoints, rollback).
  • Direct interface exposure would break encapsulation.
  • State snapshots are needed at specific points in time.
  • The cost of copying state is acceptable relative to the benefit.

Do NOT use when:

  • Object state is simple and public getters/setters are acceptable.
  • State changes are continuous and snapshots would be too frequent or large.
  • Only the latest state matters (just store current state, not history).
  • Immutable objects never need restoration (no state changes to undo).

Common Mistakes

  1. Shallow copy in memento — restoring shares mutable references; changes affect other snapshots.
  2. Caretaker modifying memento contents — violates the pattern; only Originator reads mementos.
  3. Unbounded memento stacks — memory grows indefinitely; implement a maximum history size.
  4. Exposing memento class publicly — allows external code to read private state; use inner classes or opaque interfaces.
  5. Not handling memento version compatibility — class structure changes break old mementos.
  • Command — commands store mementos before execute for undo support.
  • Prototype — cloning is an alternative snapshot mechanism; Memento is more controlled.
  • State — state objects can be mementos representing different behavioral states.
  • Iterator — caretaker can iterate over memento history for timeline UIs.
  • Flyweight — if many mementos share common state, flyweight sharing reduces memory.