Memento Pattern

The Memento is a behavioral design pattern that allows capturing and restoring an object's internal state without exposing its implementation details. It enables undo/redo operations by storing snapshots (mementos) of the object's state externally.

Key Principles

  • Encapsulation: Memento stores state privately; only originator can access it.
  • Snapshot: Memento is immutable (read-only outside originator).
  • Caretaker: Manages mementos (e.g., undo stack); doesn't inspect contents.
  • Originator: Creates and restores from mementos.

Structure

  • Originator: Object whose state we save/restore.
  • Memento: Stores snapshot of originator's state (private access).
  • Caretaker: Holds mementos (e.g., history stack).

Python Example: Text Editor with Undo

from typing import List
from datetime import datetime

# Memento (stores state)
class EditorMemento:
    def __init__(self, content: str, timestamp: datetime):
        self._content = content          # Private state
        self._timestamp = timestamp

    def get_content(self) -> str:
        return self._content

    def get_timestamp(self) -> datetime:
        return self._timestamp

# Originator (creates/restores mementos)
class TextEditor:
    def __init__(self):
        self._content = ""

    def type(self, text: str):
        self._content += text
        print(f"Current text: {self._content}")

    def save(self) -> EditorMemento:
        print("Saving state...")
        return EditorMemento(self._content, datetime.now())

    def restore(self, memento: EditorMemento):
        self._content = memento.get_content()
        print(f"Restored to: {self._content} (from {memento.get_timestamp()})")

# Caretaker (manages mementos)
class History:
    def __init__(self):
        self._mementos: List[EditorMemento] = []

    def push(self, memento: EditorMemento):
        self._mementos.append(memento)

    def pop(self) -> EditorMemento:
        if self._mementos:
            return self._mementos.pop()
        raise IndexError("No saved states")

# Usage (Client Code)
if __name__ == "__main__":
    editor = TextEditor()
    history = History()

    editor.type("Hello ")
    history.push(editor.save())

    editor.type("World!")
    history.push(editor.save())

    editor.type(" This will be undone.")

    print("\nUndoing last change...")
    editor.restore(history.pop())

    print("\nUndoing another...")
    editor.restore(history.pop())

Output:

Current text: Hello 
Saving state...
Current text: Hello World!
Saving state...
Current text: Hello World! This will be undone.

Undoing last change...
Restored to: Hello World! (from 2025-... )
Undoing another...
Restored to: Hello  (from 2025-... )

When to Use Memento

  • Undo/Redo: Editors, games, transactions.
  • Checkpointing: Save game states.
  • History Tracking: Versioned editing.
  • Avoid: Simple state or when immutability suffices.

Summary: Memento captures and externalizes an object's state in immutable snapshots, allowing restoration (e.g., undo) while preserving encapsulation.