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
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:
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
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
| Use case | The memento |
|---|---|
| Undo and redo | A snapshot per change in editors and drawing tools. |
| Form drafts | Saved state so a user can restore an autosave. |
| Game saves | Position, inventory and progress in one save file. |
| Transactions | State before an operation, restored on failure. |
| Wizards and multi-step flows | Going back to a previous step without losing data. |
| Time-travel debugging | Redux DevTools, replaying earlier states. |
| Checkpoints | Long 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.
SAVEPOINTandROLLBACK TOrestore 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
| Memento | Command | |
|---|---|---|
| Stores | The state before | The action and how to reverse it |
| Undo | Restore the snapshot | Run undo() |
| Memory | Whole state each time | Small per step |
| Reversibility needed | No, any state can be restored | Yes, 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 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- MementoProCapture and restore an object's internal state through an opaque snapshot object — the undo mechanic this page builds, made runnable.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Behavioral PatternsTen quick questions across Strategy, State, Observer, Command and the rest of the behavioral family, to check the mental model before this one.QuizTake · HardQuizTake · Hard
- Design Patterns · Architectural PatternsProA second, tougher pass on the same behavioral family, for once the first set feels easy.QuizTake · HardQuizTake · Hard
Read next
Command pattern
Turning "do this" into an object that can be queued, logged or undone, the class every action now needs, and a command that reaches into global state instead of its own.
6 min
Iterator pattern
Walking a collection without exposing how it is stored, the extra object between caller and data it costs, and the wrong answer when next() runs past the end or data mutates.
5 min