Memento
Every text editor's Ctrl+Z has to answer an awkward question: how do you restore an object to exactly how it looked a minute ago, when most of what makes it "how it looked" is private state you were never supposed to reach into from outside? The Memento pattern solves this by letting the object snapshot and restore its own state, handing out an opaque token that an external caretaker can store and hand back later — without ever peeking inside it.
⚠️ The Problem
Say you're building undo for a text editor. An Editor object tracks the current
text, the cursor position, and the selected font — all private fields, because nothing outside
the editor should be able to mutate them directly. But an UndoManager needs to be
able to save "the state right now" and restore it later. The naive fix is to make all those
fields public, or add getters/setters for every single one, so UndoManager can copy
them out into its own history stack. Now the editor's internals are exposed to any code that
wants to poke at them, encapsulation is gone, and every time you add a new piece of editor state
you have to remember to also wire it into the undo copying logic somewhere else entirely.
✅ The Solution
Let the Editor create its own Memento — a small snapshot object
containing a copy of its state — via a method like save(), and let it restore
itself from one via restore(memento). The Memento's contents can be totally opaque
to everyone except the Editor that created it. An external Caretaker (here, a
History stack) stores these Mementos in order and hands them back on request, but
never reads or modifies what's inside one — it just treats each Memento as a black box. The
Editor keeps full control over what "its state" even means; the Caretaker just manages a
timeline.
Example 1 — a text editor's undo history
class EditorMemento {
#text;
#cursor;
constructor(text, cursor) {
this.#text = text;
this.#cursor = cursor;
}
// Only the Editor class is expected to call these — the Caretaker
// never touches them, it just holds the object.
getText() { return this.#text; }
getCursor() { return this.#cursor; }
}
class Editor {
#text = "";
#cursor = 0;
type(str) {
this.#text += str;
this.#cursor += str.length;
}
save() {
return new EditorMemento(this.#text, this.#cursor);
}
restore(memento) {
this.#text = memento.getText();
this.#cursor = memento.getCursor();
}
get content() { return this.#text; }
}
class History {
#stack = [];
push(memento) {
this.#stack.push(memento);
}
pop() {
return this.#stack.pop();
}
}
const editor = new Editor();
const history = new History();
history.push(editor.save()); // snapshot: ""
editor.type("Hello");
history.push(editor.save()); // snapshot: "Hello"
editor.type(", world!");
console.log(editor.content); // "Hello, world!"
editor.restore(history.pop()); // back to "Hello"
console.log(editor.content); // "Hello"
The History caretaker never once looks at #text or #cursor
— it just pushes and pops opaque EditorMemento objects. All the knowledge of what
"editor state" consists of stays inside Editor, where it belongs.
Example 2 — a secondary context: game character save points
public class CharacterMemento
{
public int Health { get; }
public int Level { get; }
public string Location { get; }
public CharacterMemento(int health, int level, string location)
{
Health = health;
Level = level;
Location = location;
}
}
public class Character
{
private int _health = 100;
private int _level = 1;
private string _location = "Starting Village";
public void TakeDamage(int amount) => _health -= amount;
public void LevelUp() => _level++;
public void MoveTo(string place) => _location = place;
public CharacterMemento SavePoint()
=> new CharacterMemento(_health, _level, _location);
public void LoadSavePoint(CharacterMemento memento)
{
_health = memento.Health;
_level = memento.Level;
_location = memento.Location;
}
public override string ToString()
=> $"HP:{_health} Lvl:{_level} @ {_location}";
}
public class SaveSlotManager
{
private readonly Dictionary<string, CharacterMemento> _slots = new();
public void Save(string slotName, CharacterMemento memento) => _slots[slotName] = memento;
public CharacterMemento Load(string slotName) => _slots[slotName];
}
// Usage
var hero = new Character();
var slots = new SaveSlotManager();
hero.MoveTo("Dark Forest");
hero.TakeDamage(60);
slots.Save("before-boss-fight", hero.SavePoint());
hero.TakeDamage(50); // hero dies to the boss
Console.WriteLine(hero); // HP:-10 Lvl:1 @ Dark Forest
hero.LoadSavePoint(slots.Load("before-boss-fight"));
Console.WriteLine(hero); // HP:40 Lvl:1 @ Dark Forest
SaveSlotManager plays the same caretaker role as History did above —
it just files Mementos under named slots instead of a stack, which is all a "save game" system
really is.
👍 When To Use It
- You need undo/redo, checkpoints, or save points, and the object's state is meant to stay encapsulated rather than exposed via public getters/setters.
- Computing the reverse of an operation directly would be awkward or impossible, so it's easier to just capture "the whole state" before the operation and restore it wholesale.
- You want the history-keeping logic (the caretaker) to be a completely separate concern from the object whose history is being kept.
👎 When NOT To Use It
- When the object's state is large and snapshots are taken frequently — copying the full state on every change can get expensive in memory unless you add sharing/diffing tricks.
- When reversing an operation is simple to express directly (e.g. an "insert" is trivially
undone by a matching "delete") — a Command with its own
undo()is often lighter weight than snapshotting everything. - When there's no real need for history at all — a single "are you sure?" confirmation is often simpler than building a whole snapshot/restore mechanism.
❓ FAQ
How is Memento different from Command's undo()?
They solve overlapping problems differently. A Command with an undo() method
typically knows how to reverse the one specific operation it represents — an
"insert text" command undoes itself by deleting exactly the text it inserted. Memento
instead captures the object's entire state before the change and restores that whole
snapshot afterward, regardless of what operation happened. Memento is the better fit when
computing a precise inverse is hard or when many different kinds of changes all need to
share one uniform undo mechanism; Command's approach is lighter when each operation's
reverse is easy to state directly.
Doesn't this break encapsulation anyway, since the Memento holds the object's state?
The state still leaves the object, but who can read it doesn't. The Memento's
contents are only meaningful to the class that created it — in the JavaScript example above,
getText()/getCursor() are effectively a "narrow" interface intended
for Editor alone; the History caretaker never calls them. In
languages with stricter access control, the Memento's accessors can be made genuinely private
to the originator class, so the caretaker really can only push, pop, and hold — not read.
Can a Memento store just the diff instead of the full state?
Yes — nothing in the pattern requires a full copy. Storing just what changed (a diff, or a command that can regenerate the prior value) is a common optimization once full snapshots get too expensive, especially for large objects or documents. The core idea — an opaque object that lets the originator restore a prior state, managed by a caretaker that never inspects it — stays the same either way.
🙋 There Are No Dumb Questions
Q: Isn't a Memento basically just a backup copy of an object?
A: Conceptually, yes — but the pattern is specifically about who creates and reads it.
A plain backup copy is often built by outside code reaching into the object's fields. A Memento
is built by the object itself and deliberately kept opaque to outsiders, which is what keeps
the object's internal representation free to change later without breaking any code that stores
its history.
✅ Quick Check
In the Memento pattern, what is the Caretaker (like History or
SaveSlotManager) allowed to do with a Memento?
Next up: what happens when an object needs to behave completely differently depending on its current state, without a wall of if/else checks scattered everywhere? That's the State pattern.