Vasu Patel logoVasu Patel
← Patterns of Good Design
All chapters in this track
  1. 01Chapter 1: Why Patterns Exist — Vocabulary, Not a Checklist
  2. 02Chapter 2: Patterns and SOLID: How They Relate
  3. 03Chapter 3: Creational Patterns I: Factory Method & Abstract Factory
  4. 04Chapter 4: Creational Patterns II: Builder, Singleton, Prototype
  5. 05Chapter 5: Structural Patterns I: Adapter & Facade
  6. 06Chapter 6: Structural Patterns II: Decorator & Proxy
  7. 07Chapter 7: Structural Patterns III: Composite, Bridge & Flyweight
  8. 08Chapter 8: Behavioral Patterns I: Strategy & Template Method
  9. 09Chapter 9: Behavioral Patterns II: Observer & Mediator
  10. 10Chapter 10: Behavioral Patterns III: Command & Memento
  11. 11Chapter 11: Behavioral Patterns IV: State & Chain of Responsibility
  12. 12Chapter 12: Behavioral Patterns V: Iterator & Visitor
  13. 13Chapter 13: Anti-Patterns and Overuse: Recognizing "Pattern Fever"
  14. 14Chapter 14: How Dynamic and Functional Languages Solve These Problems Differently
  15. 15Chapter 15: Architectural Patterns I: MVC/MVVM & Layered/Hexagonal Architecture
  16. 16Chapter 16: Architectural Patterns II: Event-Driven Architecture & CQRS
  17. 17Chapter 17: Architectural Patterns III: The Repository Pattern
  18. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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

  1. "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.

  2. "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.

  3. "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.

  4. "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.