Snapshotting State Without Breaking Encapsulation
Many applications need undo functionality — a text editor reverting your last paragraph, a game restoring a save point, a form resetting to a prior draft. The naive approach is to expose all of an object's internal fields through public getters and setters so external code can copy and later restore them, but that permanently weakens encapsulation for every part of the system, not just the undo feature. The Memento pattern solves this by having the Originator (the object whose state needs saving) create an opaque Memento object that captures a snapshot of its own internal state, using its own privileged access to its fields. The Memento is handed to a Caretaker, which stores it (often in a stack for multiple undo levels) but cannot read or modify its contents — only the Originator that created it knows how to interpret a Memento to restore state from it.
Cricket analogy: A team's video analyst saves a specific bowling action snapshot for a player using footage only the analyst's software can fully decode, while the captain (the caretaker) just knows which snapshot to hand back for review without understanding the raw biomechanical data inside it.
The Three Roles: Originator, Memento, Caretaker
The Originator exposes two methods relevant to Memento: createMemento(), which packages current internal state into a new Memento instance, and restore(memento), which reads a given Memento's state back into itself. The Memento class itself is typically designed with a narrow interface — often just enough for the Caretaker to store and retrieve it, such as a getDate() or getLabel() method for display purposes, while the actual state payload is kept private or accessible only to the Originator (achieved in languages like C++ via friend classes, or more commonly in practice via a nested/inner class, or simply by convention in dynamically typed languages). The Caretaker is typically a stack or list of mementos representing undo history; on undo, it pops the most recent memento and passes it to originator.restore(memento), and can optionally support redo by keeping popped mementos on a separate redo stack instead of discarding them.
Cricket analogy: A DRS system's ball-tracking snapshot (the memento) is created and read only by the Hawk-Eye engine (the originator) itself, while the broadcast graphics team (the caretaker) just stores and replays the visualization without decoding the raw trajectory data.
class EditorMemento {
constructor(private readonly content: string, private readonly cursorPos: number) {}
// Only accessible to TextEditor via package-private convention
getContent(): string {
return this.content;
}
getCursorPos(): number {
return this.cursorPos;
}
}
class TextEditor {
private content = '';
private cursorPos = 0;
type(text: string): void {
this.content += text;
this.cursorPos = this.content.length;
}
createMemento(): EditorMemento {
return new EditorMemento(this.content, this.cursorPos);
}
restore(memento: EditorMemento): void {
this.content = memento.getContent();
this.cursorPos = memento.getCursorPos();
}
}
class UndoStack {
private history: EditorMemento[] = [];
save(editor: TextEditor): void {
this.history.push(editor.createMemento());
}
undo(editor: TextEditor): void {
const last = this.history.pop();
if (last) {
editor.restore(last);
}
}
}Memento in Practice: Redux, Git, and Beyond
Redux-style state management libraries implement a variant of Memento implicitly: every dispatched action produces a brand-new immutable state tree rather than mutating the previous one, so undo middleware like redux-undo can simply keep an array of prior state snapshots and swap the current pointer backward, exactly analogous to a caretaker's stack of mementos. Git commits are a similarly structured, if heavier-weight, form of Memento: each commit is an immutable snapshot of the entire repository tree, and checking out an earlier commit restores that exact snapshot. A key practical concern in real Memento implementations is the memory cost of storing full snapshots for every change — mitigations include storing deltas (diffs) instead of full copies, or using structural sharing (as in persistent data structures) so unchanged parts of the state are referenced rather than duplicated across mementos.
Cricket analogy: Cricinfo's ball-by-ball commentary archive keeps an immutable record of every delivery's outcome, letting you jump back to any exact moment of a match, much like Git commits or Redux states preserve immutable snapshots you can revert to.
Redux DevTools' time-travel debugging is a live demonstration of Memento: every action creates a new state snapshot, and 'jumping' to a previous action in the DevTools UI is literally restoring an earlier memento.
Storing a full deep copy of an object's state for every single change can consume significant memory in long-running applications with frequent state changes — profile memory usage and consider delta-based storage or structural sharing before naively snapshotting large objects on every action.
- Memento captures an object's internal state externally so it can be restored later, without exposing that state through public getters/setters.
- Three roles: Originator (creates/restores state), Memento (opaque snapshot), Caretaker (stores mementos without reading them).
- The Caretaker typically maintains a stack of mementos to support multi-level undo, and optionally a redo stack.
- Redux-style immutable state updates and Git commits are real-world variants of the Memento pattern.
- Only the Originator understands how to interpret and restore from its own Memento's contents.
- Naive full-snapshot storage can be memory-expensive; deltas or structural sharing mitigate this.
- Memento preserves encapsulation, unlike exposing all internal fields via public accessors just for undo support.
Practice what you learned
1. What is the core goal of the Memento pattern?
2. Which role in the Memento pattern is responsible for creating and restoring snapshots of its own state?
3. What is the Caretaker's relationship to the Memento's contents?
4. Which real-world system is commonly cited as a large-scale, production example of the Memento pattern?
5. What is a common concern when implementing Memento naively?
Was this page helpful?
You May Also Like
Mediator Pattern
A behavioral pattern that centralizes complex communications between related objects into a single mediator object, reducing direct coupling.
Visitor Pattern
A behavioral pattern that lets you add new operations to a family of classes without modifying those classes, by moving the operation into a separate visitor object.
Chain of Responsibility Pattern
A behavioral pattern that passes a request along a chain of handlers until one of them handles it, decoupling senders from receivers.
Related Reading
Related Study Notes in Software Engineering
Browse all study notesMicroservices Study Notes
Software Architecture · 30 topics
Software EngineeringTesting & TDD Study Notes
Software Testing · 30 topics
Software EngineeringSoftware Engineering Study Notes
Python · 40 topics
Software EngineeringGit & Version Control Study Notes
Bash · 40 topics
Software EngineeringSystem Design Study Notes
Architecture · 40 topics