All chapters in this track
- 01Chapter 1: Why Patterns Exist — Vocabulary, Not a Checklist
- 02Chapter 2: Patterns and SOLID: How They Relate
- 03Chapter 3: Creational Patterns I: Factory Method & Abstract Factory
- 04Chapter 4: Creational Patterns II: Builder, Singleton, Prototype
- 05Chapter 5: Structural Patterns I: Adapter & Facade
- 06Chapter 6: Structural Patterns II: Decorator & Proxy
- 07Chapter 7: Structural Patterns III: Composite, Bridge & Flyweight
- 08Chapter 8: Behavioral Patterns I: Strategy & Template Method
- 09Chapter 9: Behavioral Patterns II: Observer & Mediator
- 10Chapter 10: Behavioral Patterns III: Command & Memento
- 11Chapter 11: Behavioral Patterns IV: State & Chain of Responsibility
- 12Chapter 12: Behavioral Patterns V: Iterator & Visitor
- 13Chapter 13: Anti-Patterns and Overuse: Recognizing "Pattern Fever"
- 14Chapter 14: How Dynamic and Functional Languages Solve These Problems Differently
- 15Chapter 15: Architectural Patterns I: MVC/MVVM & Layered/Hexagonal Architecture
- 16Chapter 16: Architectural Patterns II: Event-Driven Architecture & CQRS
- 17Chapter 17: Architectural Patterns III: The Repository Pattern
- 18Chapter 18: Recognizing Which Pattern Fits an Interview Design Prompt
Chapter 10 of 18
Chapter 10: Behavioral Patterns III: Command & Memento
Command
The problem it solves: you want to represent "a request to do something" as an object in its own right — so it can be queued, logged, undone, or passed around — rather than as an immediate direct method call.
Before — actions are direct calls, with no way to queue, log, or undo them:
class TextEditor {
content = "";
insertText(text: string): void { this.content += text; }
deleteLastChars(count: number): void { this.content = this.content.slice(0, -count); }
}
const editor = new TextEditor();
editor.insertText("Hello"); // no record of this action; can't undo it, can't queue it, can't log it generically
After — Command turns each action into an object with a uniform interface:
interface Command {
execute(): void;
undo(): void;
}
class InsertTextCommand implements Command {
constructor(private editor: TextEditor, private text: string) {}
execute(): void { this.editor.content += this.text; }
undo(): void { this.editor.content = this.editor.content.slice(0, -this.text.length); }
}
class CommandHistory {
private history: Command[] = [];
execute(command: Command): void {
command.execute();
this.history.push(command); // every command is now uniformly recorded
}
undoLast(): void {
const command = this.history.pop();
command?.undo();
}
}
const editor = new TextEditor();
const history = new CommandHistory();
history.execute(new InsertTextCommand(editor, "Hello"));
history.execute(new InsertTextCommand(editor, " World"));
history.undoLast(); // "Hello" — undo works generically, for ANY Command, not just text insertion
console.log(editor.content);
from typing import Protocol
class Command(Protocol):
def execute(self) -> None: ...
def undo(self) -> None: ...
class InsertTextCommand:
def __init__(self, editor: "TextEditor", text: str) -> None:
self._editor = editor
self._text = text
def execute(self) -> None:
self._editor.content += self._text
def undo(self) -> None:
self._editor.content = self._editor.content[: -len(self._text)]
class CommandHistory:
def __init__(self) -> None:
self._history: list[Command] = []
def execute(self, command: Command) -> None:
command.execute()
self._history.append(command)
def undo_last(self) -> None:
if self._history:
self._history.pop().undo()
When to use it: you need undo/redo, request queuing, logging/auditing of actions, or the ability to parameterize an object with an action to perform later — text editors, task queues, transactional operations, GUI button actions.
When to avoid it: simple, immediate, non-undoable, non-queueable actions — wrapping every method call in a Command object "for consistency" adds meaningful ceremony without any of the payoffs (undo, queuing, logging) actually being needed.
Complexity cost: one class per distinct action type, plus the invoker/history machinery — moderate cost, justified specifically by needing one or more of Command's real payoffs (undo, queue, log, delay).
Memento
The problem it solves: you need to capture and restore an object's internal state (for undo, checkpoints, or snapshots) without violating its encapsulation — i.e., without exposing its private internals to the code that manages the history.
class Editor {
private content = "";
type(text: string): void { this.content += text; }
getContent(): string { return this.content; }
save(): EditorMemento {
return new EditorMemento(this.content); // Editor decides what state to snapshot — encapsulation intact
}
restore(memento: EditorMemento): void {
this.content = memento.getContent(); // only Editor and EditorMemento know the internal shape
}
}
class EditorMemento {
constructor(private readonly content: string) {} // effectively opaque to everyone except Editor
getContent(): string { return this.content; }
}
class History {
private mementos: EditorMemento[] = [];
push(memento: EditorMemento): void { this.mementos.push(memento); } // History never inspects the contents
pop(): EditorMemento | undefined { return this.mementos.pop(); }
}
const editor = new Editor();
const history = new History();
editor.type("Hello");
history.push(editor.save());
editor.type(" World");
editor.restore(history.pop()!); // back to "Hello" — History never needed to know Editor's internal structure
console.log(editor.getContent());
class EditorMemento:
def __init__(self, content: str) -> None:
self._content = content # name-mangled-ish by convention; History shouldn't read this directly
def get_content(self) -> str:
return self._content
class Editor:
def __init__(self) -> None:
self._content = ""
def type(self, text: str) -> None:
self._content += text
def save(self) -> EditorMemento:
return EditorMemento(self._content)
def restore(self, memento: EditorMemento) -> None:
self._content = memento.get_content()
[SENIOR INSIGHT] The detail that makes Memento more than "just save the state somewhere": the object being snapshotted (Editor) is the only one that creates and interprets its own memento's contents. The History class stores mementos but never looks inside them — this is exactly the OOP book's Chapter 3 encapsulation principle, applied specifically to state history. A naive alternative — exposing Editor's internal fields so an external History class can copy them directly — would work, but it breaks encapsulation to do it, coupling the history mechanism to the editor's internal representation (and breaking the moment that representation changes).
When to use it: undo/checkpoint/snapshot functionality is needed, and the object's internal representation shouldn't be exposed to the code managing the history of states.
When to avoid it: if the object's state is already simple and immutable (a plain value object), just storing copies of the state directly is simpler than introducing a dedicated memento class — Memento earns its keep specifically when there's real encapsulated internal state worth protecting from the history mechanism.
Complexity cost: low to moderate — one memento class per object type needing history, plus care about memory (storing many full-state snapshots for a large object can be expensive; some implementations store diffs instead of full snapshots for this reason).
Command and Memento together: full undo/redo
[IN PRACTICE] These two patterns are frequently paired: Command represents what action happened, and Memento captures what state to restore if that action needs undoing that can't be reconstructed by simply reversing the command's logic (e.g., a "replace entire document" command can't easily compute its own inverse — but it can snapshot the state before the replace, via Memento, and restore that snapshot to undo). Recognizing when a Command's undo needs a Memento (irreversible transformation) versus when it can compute its own inverse directly (like the InsertTextCommand example, which reverses itself) is a real design decision worth making explicit.
Common mistakes
- Wrapping trivial, non-undoable, non-queueable actions in Command "for consistency." If undo/queue/log/delay is never actually needed for an action, a direct method call is simpler.
- Breaking Memento's encapsulation by letting the history-managing class read or construct memento internals directly — this defeats the entire purpose of the pattern, reintroducing the exact coupling it exists to avoid.
- Storing full-state Mementos for large objects with high-frequency snapshots, causing real memory pressure — worth considering diff-based or interval-based snapshotting instead when this becomes a measured problem.
- Implementing
undo()as "just reverse the logic" when the operation genuinely isn't reversible (e.g., a destructive replace) — that's exactly when Memento-based state restoration is needed instead of an inverse-command approach.
Discussion / interview questions
-
"What does the Command pattern let you do that a direct method call doesn't?" Strong answer: Represents an action as an object, enabling queuing, logging, delayed execution, and (especially) undo — a direct call is immediate and has none of these properties without extra bookkeeping.
-
"How does Memento protect encapsulation, compared to a naive 'copy the object's fields into a history array' approach?" Strong answer: The object being snapshotted creates and interprets its own memento; the class managing history stores mementos opaquely without reading or depending on their internal shape — the naive alternative requires exposing internal fields, coupling the history mechanism to the object's representation.
-
"When would a Command's undo() need a Memento, versus being able to compute its own inverse?" Strong answer: When the operation isn't cleanly reversible by its own inverse logic (e.g., a destructive replace or an operation that discards information) — then a Memento captured before the operation is needed to restore state, rather than trying to compute an inverse transformation.
-
"What's a real cost consideration when using Memento for frequent snapshots of a large object?" Strong answer: Memory — storing many full-state copies can be expensive; mentions diff-based or interval snapshotting as a mitigation once this becomes a measured concern, rather than optimizing prematurely.
Chapter summary / cheat sheet
- Command: turns an action into an object, enabling queuing, logging, delayed execution, and undo — justified specifically when one of those payoffs is actually needed.
- Memento: captures and restores an object's internal state without exposing that state's representation to the code managing history — the object itself creates and interprets its own memento.
- The two often pair for full undo/redo: Command represents the action, Memento provides state restoration when the action isn't cleanly self-reversible.
- Both carry real, specific costs (ceremony for Command on trivial actions; memory for frequent full-state Mementos) that should be weighed against their specific payoffs, not applied by default.