Design Patterns, Explained Simply · Behavioral Patterns: How Objects Share Work

Memento

Save and restore an object's state as snapshots, without exposing its private fields to the code that stores the snapshots.

The problem

A drawing app needs undo for complex operations ("auto-arrange shapes") where writing a precise reverse operation is hard. The simplest idea is to save the canvas state before each operation and restore it on undo.

But the history code would then need to read and write the canvas's private fields (shape list, selection, zoom), breaking encapsulation. Every new field on the canvas means changing the history code too.

Real-world analogy

Saving a game. The game writes a save file you can't read or edit; you just keep it and later say "load this one". Only the game knows what's inside. You, the player, are the caretaker of save files, not their author.

Without the pattern

history.append({"shapes": list(canvas._shapes), "zoom": canvas._zoom})   # reaches into private fields
...
canvas._shapes = snapshot["shapes"]                                       # and writes them back

The pattern

  • Originator (Editor): the object whose state is saved. It creates mementos (save()) and restores from them (restore(memento)).
  • Memento: an immutable snapshot of the originator's state. To everyone else it's an opaque token.
  • Caretaker (History): stores mementos and decides when to save and restore, but never looks inside them.

Command (previous lesson) undoes by reversing actions; Memento undoes by restoring snapshots. Command is cheaper in memory; Memento is simpler when operations are hard to reverse.

The code

Snapshots for undo without exposing private stateTry it yourself
class Memento:  # immutable snapshot; opaque to the caretaker
    def __init__(self, text, cursor):
        self._text, self._cursor = text, cursor


class Editor:  # originator: the only one that reads mementos
    def __init__(self):
        self._text, self._cursor = "", 0

    def type(self, words):
        self._text += words
        self._cursor = len(self._text)

    def save(self):
        return Memento(self._text, self._cursor)

    def restore(self, memento):
        self._text, self._cursor = memento._text, memento._cursor

    def show(self):
        return f"'{self._text}' (cursor {self._cursor})"


class History:  # caretaker: stores snapshots, never looks inside
    def __init__(self):
        self.snapshots = []

    def push(self, memento):
        self.snapshots.append(memento)

    def pop(self):
        return self.snapshots.pop()


editor, history = Editor(), History()
editor.type("Design")
history.push(editor.save())
editor.type(" patterns")
history.push(editor.save())
editor.type(" are hard!!!")
print("now:  ", editor.show())
editor.restore(history.pop())
print("undo: ", editor.show())
editor.restore(history.pop())
print("undo: ", editor.show())

Output

now:   'Design patterns are hard!!!' (cursor 27)
undo:  'Design patterns' (cursor 15)
undo:  'Design' (cursor 6)

The history holds snapshots but has no idea what is inside them. The editor can add fields (selection, font) without the history changing.

When to use it

  • You need undo, checkpoints or rollback, and reversing operations precisely is hard.
  • You want to keep the originator's state private while still letting other code store snapshots.
  • Transactions in memory: take a snapshot, try a change, restore if validation fails.

When not to use it

  • The state is large and snapshots are frequent. Memory grows quickly. Store diffs, limit history length, or use Command with reverse operations instead.
  • The object holds external resources (open files, connections) that can't be captured in a snapshot.
  • The state is already immutable. Then "snapshots" are just references to old versions, with no pattern needed.

You've already used it

  • Editors and design tools: undo history and "revert to saved".
  • Games: save files and checkpoints.
  • Databases: savepoints (SAVEPOINT / ROLLBACK TO) restore an earlier state inside a transaction.
  • Redux DevTools / time-travel debugging: each state is a snapshot you can jump back to.

Key points

  • Memento captures and restores an object's state without exposing its internals.
  • Roles: originator (creates/restores), memento (opaque snapshot), caretaker (stores, never inspects).
  • Memento restores snapshots; Command reverses actions.
  • Watch memory use: limit history or store diffs.
  • Further reading: Refactoring Guru: Memento.

Practice questions

What are the three roles in the Memento pattern?

Answer

The originator creates snapshots of its own state and restores from them; the memento is the immutable snapshot; the caretaker stores mementos and decides when to save or restore, but never looks inside them.

When would you choose Memento over Command for undo?

Answer

When operations are hard to reverse precisely (complex transformations, auto-layout), snapshots are simpler and safer. Command uses less memory because it stores only how to reverse each action, so it suits frequent, small, reversible edits.

What is the main cost of the Memento pattern?

Answer

Memory. Each snapshot copies state, so frequent snapshots of large objects grow quickly. Limit history length, store diffs, or use immutable data structures that share unchanged parts.

Open in the app to mark lessons complete, take notes and track your progress.