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());Related Concepts
| Pattern | Relationship |
|---|---|
| Command-Pattern | Command + Memento = classic undo/redo system: Command knows what operation to undo, Memento stores the state to restore to |
| Prototype-Pattern | Prototype clones current state for future use; Memento snapshots past state for rollback — different temporal direction and different purpose |
| Iterator-Pattern | Both 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
Backlinks
Notes that link here: GoF-Patterns-MOC