Memento Pattern

Memento Pattern

Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

Intent

The Memento pattern allows an object (the Originator) to snapshot its own internal state into an opaque token (the Memento) and later restore itself from that token — without exposing its internals to the outside world. The Caretaker manages the stack of Mementos but never reads or modifies their contents. This preserves encapsulation: only the Originator knows how to create and interpret a Memento; everyone else treats it as an opaque handle.

When NOT to Use

  • State is cheap to recompute — no need to store snapshots; simply recompute on demand
  • State contains non-serialisable resources (file handles, network connections, live transactions) — a Memento of such state is either impossible or dangerous to restore
  • Unbounded undo history would consume too much memory — a different pruning strategy (e.g., command log, differential snapshot) is needed

When to Use

  • Undo/redo is required without exposing object internals to the undo manager
  • A snapshot of object state must be stored outside the object (e.g., in a Caretaker stack) and restored later
  • Implementing transactional rollback at the object level — save before a risky operation, roll back on failure

How It Works

Three participants cooperate:

  • Originator — creates a Memento from its current state (save()); restores its state from a Memento (restore(m)). Only the Originator reads Memento internals.
  • Memento — stores a snapshot of the Originator's state. Presents a narrow interface to everyone except the Originator (a "wide" internal interface to the Originator, a "narrow" opaque interface to the Caretaker).
  • Caretaker — holds the Memento stack (push, pop); passes Mementos back to the Originator when undo is requested. Never inspects or modifies a Memento's contents.

The key invariant: only the Originator reads Memento internals. The Caretaker and all other objects treat the Memento as an opaque token.

Class Diagram

classDiagram
    class Originator {
        -state
        +save() Memento
        +restore(m : Memento)
    }
    class Memento {
        -state
        -date
        +getState()
        +getDate()
    }
    class Caretaker {
        -history : Memento[]
        -originator : Originator
        +backup()
        +undo()
        +showHistory()
    }
    Originator --> Memento : creates
    Caretaker o-- Memento : stores history
    Caretaker --> Originator : requests save/restore

    note for Memento "Immutable snapshot of\nOriginator internal state.\nOpaque to Caretaker."

TypeScript Example

// Memento — TypeScript
// Source: GoF Design Patterns, p.283
 
class EditorMemento {
  constructor(readonly state: string) {}
}
 
class TextEditor {  // Originator
  constructor(private content: string = '') {}
  type(text: string) { this.content += text; }
  save(): EditorMemento { return new EditorMemento(this.content); }
  restore(m: EditorMemento) { this.content = m.state; }
  getContent() { return this.content; }
}
 
// Caretaker — holds undo stack, never reads memento internals
const editor = new TextEditor();
const history: EditorMemento[] = [];
 
editor.type('Hello');
history.push(editor.save());   // snapshot after "Hello"
 
editor.type(' World');
history.push(editor.save());   // snapshot after "Hello World"
 
editor.restore(history[0]);    // undo → back to "Hello"
console.log(editor.getContent()); // "Hello"

Java Example

// Memento — Java
// Source: GoF Design Patterns, p.283
// EditorMemento is a nested class of TextEditor — Java's idiom for the
// wide-interface (Originator reads state) vs narrow-interface (Caretaker holds token) split.
 
class TextEditor {  // Originator
    private String content;
 
    public TextEditor(String content) { this.content = content; }
    public void type(String text) { this.content += text; }
 
    public EditorMemento save() { return new EditorMemento(content); }
    public void restore(EditorMemento m) { this.content = m.getState(); }
    public String getContent() { return content; }
 
    static class EditorMemento {  // Memento — nested for wide-interface access
        private final String state;
        EditorMemento(String state) { this.state = state; }
        String getState() { return state; }  // package-private: only Originator can call
    }
}
 
// Caretaker usage
// Deque<TextEditor.EditorMemento> history = new ArrayDeque<>();
// history.push(editor.save());
// editor.restore(history.pop());
PatternRelationship
Command-PatternCommand + Memento = classic undo/redo system: Command knows what operation to undo, Memento stores the state to restore to
Prototype-PatternPrototype clones current state for future use; Memento snapshots past state for rollback — different temporal direction and different purpose
Iterator-PatternBoth traverse over state sequences (undo stack vs collection elements), but Iterator is for forward traversal, Memento is for backward rollback

Sources

  • Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, p.283

Notes that link here: GoF-Patterns-MOC