Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 40

    Memento Pattern

    Memento powers undo, snapshotting, checkpointing, and save-game.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Memento powers undo, snapshotting, checkpointing, and save-game. Originator creates Memento with state snapshot; later restore(memento). Caretaker holds Mementos but cannot read them — preserving encapsulation. Variants: Redux immutable snapshots, DB savepoints, Command undo stacks.

    The story

    A spreadsheet undo was tangled per-command hacks, frequently buggy. Memento per command grabbing copy of changed cells made undo bulletproof and enabled multi-step undo, redo, and crash recovery.

    The business problem

    Ad-hoc undo and snapshot logic breaks and leaks encapsulation:

    • Buggy undo: per-operation hacks miss edge cases.
    • Full snapshot cost: saving entire document every keystroke too expensive.
    • Encapsulation break: caretaker reading internal state directly.
    • No redo/crash recovery: single-level undo insufficient for editors.

    The problem teams faced

    Memento fits when:

    • Undo/redo needs exact state restoration at right granularity.
    • Originator must hide internal representation from caretaker.
    • Snapshots stored externally (stack, disk, other process).
    • Paired with Command for reversible operations.

    Understanding the topic

    Intent: Capture state without violating encapsulation; restore later.

    • Originator — createMemento() and restore(memento).
    • Memento — opaque snapshot; only Originator reads contents.
    • Caretaker — stores Mementos (undo stack); never inspects inside.

    Internal architecture

    Spreadsheet cell memento:

    ts
    class Spreadsheet {
    private cells = new Map<string, string>()
    createMemento(changedKeys: string[]): SpreadsheetMemento {
    const snapshot = new Map<string, string>()
    for (const key of changedKeys) snapshot.set(key, this.cells.get(key) ?? "")
    return new SpreadsheetMemento(snapshot) // opaque to caretaker
    }
    restore(m: SpreadsheetMemento) {
    for (const [key, val] of m.getCells()) this.cells.set(key, val)
    }
    }
    class UndoStack {
    private stack: SpreadsheetMemento[] = []
    push(m: SpreadsheetMemento) { this.stack.push(m) }
    pop(): SpreadsheetMemento | undefined { return this.stack.pop() }
    }

    Visual explanation

    Three diagrams cover memento flow, undo stack, and vs Command:

    Memento save / restore
    Originator
    createMemento()
    Opaque snapshot
    Caretaker stores
    Mutate state
    User edits
    restore()
    Exact rollback
    Caretaker holds snapshot but cannot read — encapsulation preserved.
    Undo stack (Caretaker)
    execute command
    Save memento first
    Push undo stack
    Caretaker
    undo()
    Pop · restore
    Redo stack
    Optional
    Command + Memento: command executes; memento captures pre-state.
    Granularity choice
    Full document?
    Expensive
    Changed cells only?
    Spreadsheet
    Command-level?
    Paired with Command
    Right granularity
    Profile memory
    Snapshot only what changed — not whole aggregate every time.

    Informative example

    Before / after — ad-hoc undo to Memento:

    ts
    // ❌ Ad-hoc undo — misses cells, breaks on merge
    let lastCells: Map<string, string> | null = null
    function editCell(key: string, val: string) {
    lastCells = new Map(cells) // full copy every time — slow
    cells.set(key, val)
    }
    function undo() { if (lastCells) cells = lastCells }
    // ✅ Memento per command — changed cells only
    class EditCellCommand {
    private memento?: SpreadsheetMemento
    execute(sheet: Spreadsheet, key: string, val: string) {
    this.memento = sheet.createMemento([key])
    sheet.setCell(key, val)
    }
    undo(sheet: Spreadsheet) {
    if (this.memento) sheet.restore(this.memento)
    }
    }

    Execution workflow

    1Memento undo workflow
    1 / 4

    Before mutate

    Originator.createMemento(changedScope).

    Command captures pre-state.

    Real-world use

    Text editor undo stacks, Git stash/commits as snapshots, DB SAVEPOINT, Redux state history, game save slots, VM checkpoints.

    Production case study

    Spreadsheet undo refactor:

    • Scenario: Per-command undo hacks buggy across 40 operations.
    • Decision: Memento per command — snapshot changed cells only.
    • Outcome: Bulletproof undo/redo; crash recovery from persisted stack.

    Trade-offs

    • Pro: encapsulation preserved; exact restore; granular snapshots.
    • Pro: caretaker simple — stack storage only.
    • Con: memory cost for many snapshots — limit stack depth.
    • Con: deep copies expensive — optimize granularity.

    Decision framework

    • Use with Command for undo/redo systems.
    • Use when originator internals must stay hidden from history store.
    • Avoid full-document snapshot on every keystroke — scope memento.
    • Limit undo stack depth in memory-constrained clients.

    Best practices

    • Memento opaque to caretaker — only Originator reads/writes snapshot.
    • Snapshot minimal changed state — not whole aggregate.
    • Persist memento stack for crash recovery when needed.

    Anti-patterns to avoid

    • Caretaker casting memento to read internals — breaks encapsulation.
    • Unbounded undo stack on large documents — memory leak class.
    • Memento storing non-serializable handles (sockets).

    Common mistakes

    • Shallow copy in memento — nested mutation aliases live state.
    • Restore without validating memento version/schema.
    • Undo stack not cleared on irreversible external side effect.

    Debugging tips

    • Compare cell count in memento vs expected changed scope.
    • Replay undo stack in test to reproduce user report.

    Optimization strategies

    • Structural sharing (immutable data) reduces memento copy cost — Redux model.
    • Limit stack to N levels; drop oldest.

    Common misconceptions

    • Memento ≠ Prototype. Memento captures for restore; Prototype clones for reuse template.
    • Command undo often uses Memento internally for state capture.
    • Git commit is memento-like snapshot with DAG history.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    4 questions
    1IntermediateQuestionMemento vs Command?+

    Answer

    Command encapsulates action with execute/undo. Memento encapsulates state snapshot. Often paired — Command undo calls restore(memento).

    Follow-up

    Who creates memento?
    2AdvancedQuestionWhy opaque memento?+

    Answer

    Preserve Originator encapsulation — Caretaker stores/restores without knowing internal structure. Prevents caretaker coupling to originator fields.

    Follow-up

    Language without private?
    3IntermediateQuestionMemento vs Prototype?+

    Answer

    Memento: snapshot for restore to prior state. Prototype: clone template for new instance from canonical prototype.

    Follow-up

    Save game?
    4AdvancedQuestionGranularity trade-off?+

    Answer

    Full doc snapshot simple but expensive. Cell-level memento cheaper but more complex. Match to operation granularity of Command.

    Follow-up

    Spreadsheet example?

    Summary

    You can implement undo stacks with Memento, preserve encapsulation, and pick snapshot granularity. Tell the spreadsheet undo refactor story — if you can do that, you own Memento.

    Ready to mark this lesson complete?Track your journey across the entire course.