Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Memento Pattern

Intent

Capture and externalize an object’s internal state so the object can be restored to this state later, without violating encapsulation.

Structure

ComponentRole
OriginatorCreates and restores mementos
MementoStores the originator’s internal state
CaretakerSafeguards the memento (never peeks inside)

Implementation

class EditorMemento:
    """Immutable snapshot of editor state."""
    def __init__(self, content, cursor):
        self._content = content
        self._cursor = cursor

    @property
    def content(self):
        return self._content

    @property
    def cursor(self):
        return self._cursor

class Editor:
    """Originator — creates and restores mementos."""
    def __init__(self):
        self._content = ''
        self._cursor = 0

    def type(self, text):
        self._content += text
        self._cursor += len(text)

    def save(self):
        return EditorMemento(self._content, self._cursor)

    def restore(self, memento: EditorMemento):
        self._content = memento.content
        self._cursor = memento.cursor

    @property
    def content(self):
        return self._content

class History:
    """Caretaker — manages undo/redo stack."""
    def __init__(self):
        self._undo_stack = []
        self._redo_stack = []

    def push(self, memento):
        self._undo_stack.append(memento)
        self._redo_stack.clear()  # new action invalidates redo

    def undo(self):
        if self._undo_stack:
            m = self._undo_stack.pop()
            self._redo_stack.append(m)
            return m
        return None

    def redo(self):
        if self._redo_stack:
            m = self._redo_stack.pop()
            self._undo_stack.append(m)
            return m
        return None

# Usage
editor = Editor()
history = History()
editor.type('Hello')
history.push(editor.save())
editor.type(' World')
history.push(editor.save())
print(editor.content)  # 'Hello World'
editor.restore(history.undo())
print(editor.content)  # 'Hello'

Command-Query Separation (CQS)

The memento pattern pairs naturally with CQS: commands (mutations) produce a memento before executing, enabling undo. Queries return data without side effects and are not undoable.

PrincipleExample
Commandeditor.type(text) — mutates state, push memento
Queryeditor.content — returns value, no mutation

Interview Questions

Q: How does the memento pattern preserve encapsulation? A: The caretaker only holds an opaque memento object. The originator creates and interprets the memento’s contents. The caretaker never reads or modifies the internal state stored in the memento.

Q: What’s the memory cost of unlimited undo? A: Each memento stores a full state snapshot, so O(n × s) where n is the number of actions and s is the state size. Mitigations: store diffs instead of full snapshots, limit history depth, or use command pattern with inverse operations.

Q: Memento vs Command pattern for undo? A: Memento snapshots state directly — simpler but memory-heavy. Command pattern stores operations and reverses them — more memory-efficient but requires implementing inverse operations for each command.

References