LESSON 7.6
Memento
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.
15 min read
Real-world analogy
A video game checkpoint or quick-save feature demonstrates the Memento pattern. Before facing a dangerous boss battle, a player presses the quick-save key (F5). The game engine packages your character's current health, ammunition, inventory, and exact coordinates into an opaque save-state file (the memento). The operating system or cloud save manager (the caretaker) stores this file in a folder without knowing how the game engine's internal physics or AI routines work. If your character perishes, you load the checkpoint, and the game engine (the originator) restores you to that exact moment in time.
The problem
You are building a graphics canvas editor. Users can draw shapes, change fill colors, move layers, and resize objects. You need to provide a multi-level Undo and Redo feature.
If you attempt to capture the canvas state externally without the Memento pattern, you face an architectural dilemma:
- Violating Encapsulation: If you make the canvas's internal private state fields (vertex coordinates, selection caches, dirty layer flags) public so an
UndoManagercan copy them, you leak internal implementation details and violate encapsulation. - Brittle Coupling: If the
UndoManagerknows the exact fields ofCanvas, every change to the internal drawing engine forces a rewrite of the undo system:
// Naive implementation: Breaching encapsulation to save state externally
class Canvas {
// Public exposure of private data structures breaks encapsulation!
layers = [];
selectedLayerId = null;
brushColor = "#000000";
}
class UndoManager {
#history = [];
save(canvas) {
// Caretaker reaches directly into internal private fields
const snapshot = {
layers: JSON.parse(JSON.stringify(canvas.layers)),
selectedLayerId: canvas.selectedLayerId,
brushColor: canvas.brushColor
};
this.#history.push(snapshot);
}
restore(canvas) {
const last = this.#history.pop();
// Directly mutating external canvas fields
canvas.layers = last.layers;
canvas.selectedLayerId = last.selectedLayerId;
canvas.brushColor = last.brushColor;
}
}public class Example {
static class Canvas {
public java.util.List<String> layers = new java.util.ArrayList<>();
public String brush = "#000000"; // Leaked public fields
}
static class UndoManager {
public void save(Canvas canvas) {
Exposing private fields so an external history manager can serialize them breaks object encapsulation and makes it impossible to change internal representations without rewriting the history manager.
The solution
The Memento pattern allows an object (the Originator) to capture its own internal state into a dedicated snapshot object (the Memento). The snapshot is handed to an external Caretaker (such as an undo manager), which stores it safely without inspecting or modifying its contents. Later, the caretaker passes the memento back to the originator to restore its state.
- Originator (
TextEditor): The object whose state needs to be saved and restored. Creates mementos and uses them to revert state. - Memento (
EditorSnapshot): An opaque value object storing the originator's state. Provides a narrow interface to the caretaker and a wide interface to the originator. - Caretaker (
HistoryManager): Stores and manages a history of mementos (undo/redo stacks), but never accesses or mutates the memento's payload.
Step-by-step implementation
Step 1: Define the opaque memento
The memento encapsulates the state snapshot. In languages with package-private access (like Java), internal getters are hidden from the caretaker.
Step 2: Implement the originator with save and restore
The originator creates mementos holding its private state and consumes them to restore itself.
Step 3: Implement the caretaker history manager
The caretaker stores mementos in an undo stack without reading their internal properties.
class HistoryManager {
#history = [];
push(memento) {
this.#history.push(memento);
}
pop() {
return this.#history.pop() ?? null;
}
}The complete example
Here is the complete, runnable implementation demonstrating a stateful editor with multi-level undo snapshots.
--- Step 1: Initial Draft ---
Current Text: "Design patterns provide reusable solutions."
--- Step 2: Second Sentence ---
Current Text: "Design patterns provide reusable solutions. The Memento pattern captures internal state."
--- Step 3: Accidental Deletion ---
Current Text (after accident): ""
--- Step 4: Reverting State via Memento ---
[History] Reverted to snapshot from 2026-10-19T12:00:00.000Z
Current Text: "Design patterns provide reusable solutions. The Memento pattern captures internal state."
--- Step 5: Second Revert ---
Variations
Wide vs narrow interfaces
To maintain encapsulation, GoF specifies two interface views for a Memento:
- Narrow Interface (Caretaker View): Exposes only metadata (e.g.
getTimestamp(),getName()). The caretaker cannot inspect or modify the underlying state payload. - Wide Interface (Originator View): Exposes all internal fields. In Java, this is achieved by making the memento class or constructor package-private to the originator, or by nesting the memento as a private inner class inside the originator.
Memory limits and copy-on-write deltas
Storing complete deep snapshots on every modification exhausts memory rapidly:
- Bounded Ring Buffers: Caretakers should enforce a fixed capacity (e.g. max 50 snapshots), evicting the oldest snapshot when capacity is reached.
- Delta Compression: Instead of saving the full object graph, the memento records only the diff/delta of what changed since the previous snapshot (similar to Git commit trees).
Where you've already used it
- HTML5 History API:
window.history.pushState(state, title, url)andwindow.onpopstatesave and restore serializable page state objects. - Database Savepoints:
SAVEPOINT sp1creates a transaction memento, andROLLBACK TO SAVEPOINT sp1reverts uncommitted queries back to that checkpoint. - Redux DevTools Time Travel: Redux logs every state tree snapshot to an array, allowing developers to slide backward and replay actions interactively.
- Git Commit Trees: Each Git commit object acts as an immutable tree memento referencing blob hashes.
Pros and cons
Advantages
- Preserves Encapsulation: Captures an object's state without exposing private fields to external history managers.
- Simplifies Originator: The originator does not need to manage history stacks, timestamps, or persistence queues.
- Clean Rollback Mechanism: Provides an intuitive, robust rollback and undo architecture.
Disadvantages
- Memory Consumption: Storing frequent full snapshots of large object graphs can quickly lead to high memory usage.
- Caretaker Lifecycle Overhead: The caretaker must actively clean up and truncate history to avoid retaining obsolete snapshots.
When not to use it
Do not use Memento if the cost of cloning or capturing the object's state in memory exceeds your system's RAM budget. If memory is tight, prefer Command with inverse operations (cmd.undo()), which recalculates state dynamically rather than storing full memory snapshots.
Related patterns
| Pattern | State Capture Model | Responsibility |
|---|---|---|
| Memento | Passive state snapshot | Captures state without violating encapsulation |
| Command | Active executable operation | Reverses state by executing inverse operations |
| Iterator | Cursor traversal state | Can use Mementos to save current iteration position |
| Prototype | Exact independent clone | Duplicates object for independent execution |
Memento: editor snapshot history
Capture and restore editor state snapshots using the Memento pattern, ensuring snapshots are completely immutable and safe from external mutation.
Click Run tests to evaluate your solution against the behavior and structural test suite.
Interview questions
How does Memento preserve encapsulation compared to saving state via getters and setters? Instead of forcing the Originator to expose private fields via public getters and setters (which leaks internals to the entire codebase), only the Memento holds the data, and only the Originator that produced the memento can read its contents.
How does Memento differ from Command in implementing Undo?
Command achieves undo computationally by executing the inverse operation (e.g., executing delete to reverse an insert). Memento achieves undo by state restoration, replacing the entire current state with a saved snapshot.
How do you prevent Memento from consuming excessive RAM in production applications? Use a bounded ring buffer to cap history length (e.g. keeping only the last 30 snapshots), serialize inactive snapshots to disk, or record copy-on-write delta differences instead of full object graph clones.
Can a Memento be serialized and sent over a network? Yes. In distributed systems or long-running workflows, mementos are frequently serialized to JSON or Protobuf and stored in Redis or database tables to support resilient process recovery after service restarts.
Try it
Versioned Key-Value Store LLD
Implement immutable snapshots, compare-and-swap and tombstones in a small MVCC model.