Save an object’s state so you can restore it later, without the saver being able to poke at the internals.
The three roles
/** Opaque on purpose: the caretaker can hold it and cannot read it. */class EditorMemento { constructor( private readonly content: string, private readonly cursor: number, ) {}
// No getters. Only Editor knows what is inside, via the restore method below.}
class Editor { // originator private content = ''; private cursor = 0;
save(): EditorMemento { return new EditorMemento(this.content, this.cursor); }
restore(memento: EditorMemento) { // In TypeScript, private is per-class, so Editor cannot read another // class's privates — hence the memento exposing a package-level accessor, // or (more usually) being a nested class. ({content: this.content, cursor: this.cursor} = memento.read()); }}
class History { // caretaker private states: EditorMemento[] = [];
push(memento: EditorMemento) { this.states.push(memento); } pop(): EditorMemento | undefined { return this.states.pop(); }}Originator makes and accepts snapshots. Memento is the snapshot. Caretaker stores them and never looks inside.
hello
cursor 5
caretaker — holds, cannot read
Each save is an opaque blob. The caretaker can hold it, list it, and hand it back — it has
no accessor for text or cursor, so it cannot come to depend on
how the editor stores things.
The cost, which is usually the deciding factor
A memento is a full copy. Undo depth of 100 on a 10 MB document is a gigabyte.
That is why editors that actually ship rarely use plain mementos:
- Command stores what changed rather than everything — bytes per keystroke, not megabytes.
- Structural sharing copies only the path from the root to the change and reuses the rest. Immutable data structures do this, and it is why persistent collections make undo cheap.
- Periodic snapshots plus a log — snapshot every hundred operations, replay commands forward from the nearest one. This is what databases do, and it is the right answer at scale.
When it is the right choice
Memento is genuinely simpler than Command when:
- The state is small — a form’s values, a game’s checkpoint, a wizard step.
- Changes are hard to invert individually. If a single edit reshuffles
everything, writing
undo()for it is harder than keeping a copy. - You need to save at arbitrary points rather than after every action.
The last one is the strongest reason. “Save game” is a memento; “undo” is usually a command.