Memento Pattern
Captures and externalizes an object's internal state so it can be restored later, without violating encapsulation. Memento is the pattern behind undo mechanisms, checkpoints, and snapshots, allowing you to roll back an object to a previous state.
✦ Problem
To create a snapshot of an object's state, you'd need to access all its fields, including private ones. Making fields public to enable snapshots breaks encapsulation. Storing state externally in a format tied to the object's internals couples the snapshot code to every change in the object's structure.
✦ Solution
Let the object itself produce a snapshot of its state (a memento). The memento is an opaque object—no one except the originator can access the stored state. A caretaker (such as an undo manager) holds mementos but never examines or modifies their content. When undo is needed, the caretaker passes the appropriate memento back to the originator, which restores its state.
Participants
| Role | Responsibility |
|---|---|
| Memento | Stores the internal state of the Originator; protects against access by objects other than the originator |
| Originator | Creates a memento containing a snapshot of its current state; uses the memento to restore itself |
| Caretaker | Keeps mementos but never operates on or examines their contents |
TypeScript Example
interface EditorMemento {
readonly timestamp: number;
}
class DocumentEditor {
private content = "";
private cursorPos = 0;
type(text: string) {
this.content =
this.content.slice(0, this.cursorPos) +
text +
this.content.slice(this.cursorPos);
this.cursorPos += text.length;
}
save(): EditorMemento {
return {
timestamp: Date.now(),
...(Object.freeze({ _content: this.content, _cursor: this.cursorPos }) as any),
};
}
restore(memento: EditorMemento) {
const m = memento as any;
this.content = m._content;
this.cursorPos = m._cursor;
}
toString() {
return `"${this.content}" (cursor: ${this.cursorPos})`;
}
}
class UndoManager {
private stack: EditorMemento[] = [];
save(memento: EditorMemento) { this.stack.push(memento); }
undo(): EditorMemento | undefined { return this.stack.pop(); }
}
const editor = new DocumentEditor();
const undoMgr = new UndoManager();
undoMgr.save(editor.save());
editor.type("Hello");
undoMgr.save(editor.save());
editor.type(" World");
console.log(editor.toString()); // "Hello World"
const prev = undoMgr.undo();
if (prev) editor.restore(prev);
console.log(editor.toString()); // "Hello"Real-World Example
Text editors (Ctrl+Z) and graphic design tools like Figma save document states as mementos. Database transaction logs and WAL (Write-Ahead Logging) are memento implementations for crash recovery. Version control systems capture repository snapshots. Browser history stores page states for back/forward navigation. Game save systems store the entire game state as a memento.
✓ When to Use
- •You need to save and restore an object's state (undo, checkpoints, snapshots)
- •Direct access to the object's fields would expose implementation details and break encapsulation
- •You want to create snapshots of objects without coupling to their internal structures
✗ When Not to Use
- •The object's state is trivially small and easy to recreate from scratch
- •Creating mementos frequently would consume too much memory
- •The programming language has built-in serialization that makes custom snapshots unnecessary
Understanding the Memento Pattern
The Memento pattern is one of the most important behavioral design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.
Captures and externalizes an object's internal state so it can be restored later, without violating encapsulation. Memento is the pattern behind undo mechanisms, checkpoints, and snapshots, allowing you to roll back an object to a previous state. The pattern involves the following key participants: Memento, Originator, Caretaker. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.
In TypeScript and modern JavaScript development, the Memento pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Memento implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.
Knowing when to apply the Memento pattern is as important as knowing how. Common use cases include: You need to save and restore an object's state (undo, checkpoints, snapshots); Direct access to the object's fields would expose implementation details and break encapsulation; You want to create snapshots of objects without coupling to their internal structures. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.
The Memento pattern is related to several other design patterns: Command, Iterator, Prototype. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.
Related Patterns
Encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requ…
Provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation…
Creates new objects by copying an existing object, known as the prototype. This pattern is useful when creating an insta…
More Behavioral Patterns
Explore All Design Patterns
Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.