codejump

AcademyDesign Patterns

Memento pattern

Turning "go back to this" into an object kept separate from undo logic, the snapshot cost that grows with saved state, and a setter turning a saved state into a mutable copy.

6 min readUpdated

3 exercises

Waiting at the end of this page.

Memento turns a snapshot into a value the same way Command turns an action into one: a way to capture "go back to this" without the object exposing its internals to whatever is doing the restoring.

A Memento captures an object's internal state in a separate, opaque value, so that "restore to how it was" doesn't require the object exposing its internals to whatever is doing the restoring. Command turns an action into an object; Memento turns a snapshot into one.

The problem it solves

You are building an editor with undo. Before each change you need to remember what the content looked like, so you can go back.

The obvious approach is to let a history class read the editor's fields and write them back:

TypeScript
history.push({ content: editor.content, cursor: editor.cursor, selection: editor.selection });
// later
editor.content = saved.content;
  • Encapsulation breaks. The history needs to know every private field the editor has.
  • It breaks silently. Add a field to the editor, forget it in the history, and undo restores a half-correct state.
  • Anyone can tamper with a snapshot. It is a plain object, open to modification.

The fix is to let the editor create its own snapshot object and restore itself from one. The history stores snapshots without looking inside them.

Structure

The editor creates a snapshot of itself. The history stores it without reading it, and hands it back to restore
The editor creates a snapshot of itself. The history stores it without reading it, and hands it back to restore

Three parts:

  • The originator (Editor): the object whose state is saved. It creates mementos and restores from them.
  • The memento (EditorMemento): an opaque value holding the saved state.
  • The caretaker (the history): stores mementos and decides when to save or restore, but never inspects them.
class EditorMemento {
  constructor(content) { this.content = content; }
}

class Editor {
  constructor() { this.content = ''; }
  type(text) { this.content += text; }
  save() { return new EditorMemento(this.content); }
  restore(memento) { this.content = memento.content; }
}

// const editor = new Editor();
// editor.type('Hello');
// const saved = editor.save();
// editor.type(' world');
// editor.restore(saved);
// editor.content → 'Hello'

The history sees EditorMemento objects and nothing else. Only the editor knows what is inside and how to use it, so the editor is free to change its fields without breaking undo.

Where it is used in real systems

Each change saves a snapshot, and undo restores the previous one
Each change saves a snapshot, and undo restores the previous one
Use caseThe memento
Undo and redoA snapshot per change in editors and drawing tools.
Form draftsSaved state so a user can restore an autosave.
Game savesPosition, inventory and progress in one save file.
TransactionsState before an operation, restored on failure.
Wizards and multi-step flowsGoing back to a previous step without losing data.
Time-travel debuggingRedux DevTools, replaying earlier states.
CheckpointsLong computations that can resume from a saved point.

Mementos you already use

  • Redux DevTools time travel. Every state is a snapshot you can return to.
  • `history.pushState` and the browser back button store a state object per entry, restored on navigation.
  • Git commits. Each commit is a snapshot of a whole tree, and checkout restores one.
  • Database snapshots and savepoints. SAVEPOINT and ROLLBACK TO restore a transaction's earlier state.
  • Immutable state libraries. Immer and Immutable.js make each state cheap to keep, so a history of them is a list of mementos.

The cost of snapshots

Mementos are simple, but each one holds a copy of the state.

  • Memory grows with history. Ten thousand undo steps of a large document is a lot of memory. Cap the history, or store only the difference between states.
  • Copy carefully. A snapshot that shares a nested object with the live state is corrupted by later changes. Deep copy, or use immutable data. This is the same trap as in the Prototype pattern.
  • Snapshots can go stale. If a saved state refers to something that no longer exists, restoring it can fail.

For large states, combine ideas: store commands with undo() (Command) for small changes, and snapshots (Memento) at intervals.

Memento or Command for undo

MementoCommand
StoresThe state beforeThe action and how to reverse it
UndoRestore the snapshotRun undo()
MemoryWhole state each timeSmall per step
Reversibility neededNo, any state can be restoredYes, every action needs an inverse

Many editors use both.

When not to use it

  • The state is huge and changes often. Full snapshots are too expensive. Use commands or diffs.
  • Changes are cheap to reverse. If each action has a trivial inverse, a command is lighter.
  • The state contains things that cannot be copied. Open connections, file handles and timers should not be in a snapshot.
  • You only need the latest value. If nothing needs to go back, do not keep a history.

What interviewers ask

  • "What is the Memento pattern?" Capturing and restoring an object's state without exposing its internals.
  • "Who are the three roles?" Originator, memento, caretaker.
  • "Memento versus Command for undo?" Snapshots restore state. Commands reverse actions.
  • "What is the main cost?" Memory for stored snapshots, and correct copying of nested data.
  • "Give a real example." Editor undo, Redux time travel, game save files.

The mistake to avoid

A Memento that holds a reference to live state instead of a copy. In the example, new EditorMemento(this.content) is safe because a string is immutable. If the state were an array or object and the memento kept the same reference, every later change would rewrite the "saved" snapshot, and undo would restore the present, not the past.

The mechanism above, an object that packages its own state and later reads it back, is the same in JavaScript, Python or Java. Only the syntax changes.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.