Memento Pattern
Memento works by having the object that owns the state (the originator) hand out a sealed snapshot that only it can read back — so a third party can hold and reorder snapshots for undo/redo without ever touching the originator's private fields, and the originator stays free to change its internal layout without breaking everyone who stores its history.
That last clause is the whole point of the pattern, and it is exactly what a careless implementation throws away. If your snapshot class exposes public getters that the originator reads field-by-field to restore itself, you have built a plain data-transfer object with a fancy name: the snapshot's shape is now part of your public API, anyone can read it, and the encapsulation Memento promised is gone.
The two mistakes the naive version makes
The common textbook version of this pattern (and the earlier draft of this page) contains two concrete defects worth naming, because both are easy to ship and hard to spot in review.
Defect 1 — the encapsulation leak. The snapshot exposes getLevel(), getScore(), getInventory() as public methods, and Game.loadSave() calls them one at a time:
// LEAKY restore — the caretaker (and everyone else) can read the snapshot
this.level = save.getLevel();
this.score = save.getScore();
this.inventory = save.getInventory();This is the wide interface Memento is supposed to avoid. The save's internal structure is now visible to any holder, and if Game later adds a field (say checkpointTimestamp) the snapshot's public surface must grow too, dragging every caller along. A real memento is opaque: the caretaker sees a featureless token; only the originator can crack it open.
Defect 2 — the off-by-one undo. play() mutates state and then pushes the new state; undoLastPlay() pops that same top entry and restores it. So your first undo restores the state you are already in — a visible no-op — and you only reach the previous state on the second undo. The fix is to capture the snapshot before mutating (so the stack holds states you can return to), and to leave the current live state out of the history.
A correct, opaque implementation (Java)
The key move: Memento is a sealed type with no public accessors. The caretaker can hold it and pass it around but cannot read it. The originator restores by handing the memento back its own state through a package-private (or inner-class) channel that nobody else can call. Here we use a Java inner class so the memento can reach Game's privates directly.
import java.util.*;
class Game {
private int level = 0;
private int score = 0;
private List<String> inventory = new ArrayList<>();
// OPAQUE memento: a sealed snapshot. No getters — the caretaker
// sees only the empty marker interface below.
public interface Memento {}
private final class Snapshot implements Memento {
private final int level, score;
private final List<String> inventory;
private Snapshot(int level, int score, List<String> inv) {
this.level = level;
this.score = score;
this.inventory = new ArrayList<>(inv); // defensive copy
}
}
// Capture the CURRENT state BEFORE any further mutation.
public Memento save() {
return new Snapshot(level, score, inventory);
}
// Only Game can read a Snapshot. The cast is safe: a Memento we
// didn't create can't reach our private fields anyway.
public void restore(Memento m) {
Snapshot s = (Snapshot) m;
this.level = s.level;
this.score = s.score;
this.inventory = new ArrayList<>(s.inventory);
}
public void play(int dLevel, int dScore, List<String> newItems) {
level += dLevel;
score += dScore;
inventory.addAll(newItems);
}
public String status() {
return "Level=" + level + " Score=" + score + " Inv=" + inventory;
}
}
// Caretaker: keeps history, never inspects a Memento's contents.
class History {
private final Deque<Game.Memento> stack = new ArrayDeque<>();
public void push(Game.Memento m) { stack.push(m); }
public boolean canUndo() { return !stack.isEmpty(); }
public Game.Memento pop() { return stack.pop(); }
}
public class Solution {
public static void main(String[] args) {
Game game = new Game();
History history = new History();
history.push(game.save()); // snapshot BEFORE play 1: L0 S0 []
game.play(1, 100, List.of("Sword")); // now L1 S100
history.push(game.save()); // snapshot BEFORE play 2: L1 S100
game.play(2, 200, List.of("Bow")); // now L3 S300
System.out.println(game.status()); // Level=3 Score=300 Inv=[Sword, Bow]
game.restore(history.pop()); // back to L1 S100
System.out.println(game.status()); // Level=1 Score=100 Inv=[Sword]
}
}Why the naive version was wrong. (1) Snapshots are now opaque — History can store and reorder them but cannot read a single field, so Game's layout stays private. (2) We push the snapshot before each play(), so the stack holds states we can return to; the first undo lands on the genuinely previous state instead of the no-op the off-by-one version produced.
Worked trace: two plays, one undo
Watch the live state and the history stack side by side. The previous version's bug shows up at the moment of undo; the corrected version restores the real prior state.
| Step | Live state (Game) | History stack (top → bottom) |
|---|---|---|
| start | L0 S0 [] | (empty) |
| save() | L0 S0 [] | [L0 S0 []] |
| play(1, 100, Sword) | L1 S100 [Sword] | [L0 S0 []] |
| save() | L1 S100 [Sword] | [L1 S100 [Sword]] , [L0 S0 []] |
| play(2, 200, Bow) | L3 S300 [Sword, Bow] | [L1 S100 [Sword]] , [L0 S0 []] |
| restore(pop()) | L1 S100 [Sword] | [L0 S0 []] |
One undo returns you to L1 S100 [Sword] — the state just before the second play. In the off-by-one version the stack top was L3 S300 (the current state), so the same undo restored what you already had and appeared to do nothing.
Pitfalls
- Shallow snapshots alias live state. Storing the same
Listreference instead of a copy means every laterplay()mutates your saved history in place — your undo restores the present. The defensivenew ArrayList<>(inv)in both the constructor andrestore()is load-bearing, not decoration. For nested mutable objects you need a deep copy or genuinely immutable value objects. - Unbounded history is a memory leak. A snapshot per keystroke in an editor, or per frame in a game, grows without bound. Cap the stack (drop the oldest), snapshot at coarser granularity (per command, per checkpoint), or store deltas instead of full states.
- Full-state snapshots are expensive for large originators. Copying a 50 MB document on every edit is wasteful. Command pattern's inverse operations, or copy-on-write / structural sharing, avoid duplicating the unchanged bulk.
- Resources don't snapshot. Open sockets, file handles, and thread state can't be meaningfully captured and replayed. Memento is for value-like state, not live I/O.
- Redo needs a second stack. Undo alone is one stack. For redo, popping the undo stack must push onto a redo stack, and a fresh edit must clear redo — forgetting that lets users "redo" into a branch that no longer exists.
When to use it — and when not to
Reach for Memento when you need to restore an object to an exact earlier state, the state is mostly value-like (copyable), and you want the history-keeping code to be decoupled from the object's internals. The decision signal is: "I need a snapshot I can return to, and I refuse to expose the object's guts to do it." Undo/redo, save-games, transaction rollback, and wizard "back" buttons all fit.
Memento vs. Command (the usual rivalry). Command stores the operation and its inverse ("insert 'x' at position 9" → undo = "delete position 9"); Memento stores the result state ("the document was exactly this"). Gain with Command: tiny per-step memory, since you keep only the delta, and you get a replayable audit log for free. Cost: every operation needs a correct, well-tested inverse — and some operations have no clean inverse (a lossy filter, a random shuffle). Gain with Memento: restore is trivially correct because it overwrites with a known-good snapshot, no inverse logic to get wrong. Cost: each snapshot copies the whole relevant state, so memory grows with history length × state size.
Choose Memento when restoring exact state is easy but inverting operations is hard or impossible; prefer Command when state is large but each operation has a cheap, reliable inverse — or when you also need a replayable log of what happened, not just snapshots of where you landed.
Put numbers on the trade-off. Take a 200 KB document and a 50-step undo history. Full Memento snapshots cost 50 × 200 KB ≈ 10 MB of history (plus the live copy). Command inverse deltas, where an average edit is a few hundred bytes, cost 50 × ~200 B ≈ 10 KB — three orders of magnitude less. That gap is the whole reason large editors keep the undo log as commands and drop back to Mementos only as periodic restore checkpoints: you pay 10 KB for the common case and reserve the 200 KB snapshot for the rare "jump back to a checkpoint" that would otherwise mean replaying thousands of commands.
Concrete pick. A pixel paint app where the user applies a Gaussian blur: there is no exact inverse of a blur, so Command-style undo would have to store the pre-blur pixels anyway — which is a memento. Take Memento and snapshot the affected region. A plain text editor, by contrast, where edits are insert/delete with obvious inverses and documents get huge: prefer Command and keep deltas. (Many real editors blend both: Command for the log, periodic Mementos as restore checkpoints so undo doesn't have to replay from the beginning.)
Memento vs. serialization (the other thing people reach for). Serializing the object — Serializable, JSON, protobuf — also produces a snapshot of state, so the natural question is "why not just serialize it for undo?" The difference is the boundary each is built to cross. A memento lives in memory, inside one process, for the life of the session, and is opaque: nobody but the originator can read it. A serialized blob is built to cross a boundary — disk, network, another JVM — and is therefore inspectable and versioned: it externalizes the object's fields into a public wire format, which is exactly the wide-interface leak Memento exists to prevent, and it drags in schema-evolution cost (serialVersionUID, migrating old bytes into a new class shape) because a persisted snapshot can outlive the code that wrote it. A memento never outlives the process, so it needs no versioning and pays no marshalling CPU — just an in-memory copy. The decision: use a Memento for fast, opaque, in-process undo/redo; use serialization when the snapshot must be durable (a save-file that survives a crash), portable (sent over a wire), or inspectable by tooling. They compose — a real editor often keeps in-memory Mementos for live undo and serializes a checkpoint to disk for crash recovery, i.e. the memento's restore is in-process while a separate persistence layer handles durability.
Don't reach for Memento at all when a single "reset to defaults" suffices (a constructor or a saved initial config is simpler), or when the state is trivially reconstructable from inputs you already keep.
Takeaways
- The pattern's entire value is the opaque snapshot: if the caretaker can read the memento's fields, you've leaked encapsulation and built a DTO, not a memento. Give it a marker interface and a private restore channel.
- Capture the snapshot before mutating, and keep the live state out of the history — otherwise undo is off by one and the first undo is a no-op.
- Copy defensively on both save and restore; aliasing mutable state quietly destroys your history.
- It trades memory for correctness: restore is dead simple, but each snapshot costs the size of the state. When state is large and operations have cheap inverses, prefer Command's deltas.
Sources: Gamma, Helm, Johnson & Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (the original Memento intent — an opaque snapshot with a wide interface for the originator and a narrow one for the caretaker); Refactoring.Guru, "Memento"; the Java inner-class technique for an opaque memento follows Joshua Bloch, Effective Java (nested classes and defensive copying). Re-authored and deepened for this guide: fixed the encapsulation leak (the snapshot's public getters read directly by the originator) by making the memento opaque with a private restore channel, and corrected the off-by-one undo by snapshotting before mutation and keeping live state out of the history.
🤖 Don't fully get this? Learn it with Claude
Stuck on Memento Pattern? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Memento Pattern** (OO & Low-Level Design) and want to truly understand it. Explain Memento Pattern from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Memento Pattern** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Memento Pattern** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Memento Pattern** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.