Skip to main content

Memento

complexitypopularity

Capture and restore an object's previous state without exposing a single one of its private fields to the outside world.

The problem

You are building a text editor and, like every app since 1984, it needs undo. The direct approach looks obvious: before each operation, copy the state of everything into storage, and on undo, copy it back.

Then you try to write the copying code and hit a wall. The editor keeps its text, cursor coordinates, scroll position, and selection width in private fields, exactly where they belong. An outside class cannot read them. So you shrug and make everything public.

Now two things are broken. The editor's internals are exposed to the entire program, and the snapshot code is welded to its field list, so every refactor of the editor drags the history class along with it. Restricting access makes snapshots impossible; allowing access makes the design fragile. That is not a trade-off, that is a dead end.

The solution

The dead end exists because the wrong object is doing the work. A history class that reads the editor's private fields is trespassing. Stop trespassing and ask instead.

Memento moves snapshot duty to the object that owns the state, called the originator. It produces a memento: a small immutable object holding a copy of that state, which nobody else can read. Other objects, called caretakers, may hold the memento, stack it, and hand it back for restoration, but the contents stay sealed. A caretaker sees metadata at most - when the snapshot was taken, what operation it precedes.

History (caretaker)BoldCommandEditor (originator)Snapshot (memento)makeBackup()1createSnapshot()2new Snapshot(this, text, curX, curY, width)3backup = snapshot4execute()5undo()6restore()7setText(...) / setCursor(...)8
  1. 1The user hits Bold. Before anything mutates, the caretaker tells the command to take a safety copy.
  2. 2The command does not read the editor. It asks the editor to snapshot itself, which is the entire trick of the pattern.
  3. 3The editor pours its private fields into a fresh memento through the constructor. Private data never leaves the family.
  4. 4The command stashes the memento as an opaque token. It could not read the text inside it if it tried.
  5. 5Now the command actually does its job and scribbles all over the editor state.
  6. 6The user regrets everything. The history pops the most recent command and asks it to undo.
  7. 7The command hands the memento back. Restoration logic lives in the memento, not in the caretaker.
  8. 8The memento pushes the old values back into its own editor through setters. Time travel, encapsulation intact.

Structure

Languages with nested classes get the strictest version: nest the memento inside the originator so the outer class can read its fields while everyone else sees an opaque handle. Elsewhere you extract a marker interface that declares only metadata, and caretakers hold that. A third variant, shown below, links each memento to the originator that made it and puts restore() on the memento, which frees the caretaker from knowing the originator type at all.

Editor- text, curX, curY, selectionWidthcreateSnapshot(): SnapshotsetText(text)setCursor(x, y)ORIGINATORSnapshot- editor: Editor- text, curX, curY, selectionWidthrestore()MEMENTOCommand- backup: SnapshotmakeBackup()undo()CARETAKERHistory- stack: Command[]push(command)undo()CARETAKER
usescreates

Code

Same example three ways: a text editor with an undo stack, where each command carries the snapshot it took before running.

// The history class reaches into the editor and rifles through its pockets.
class History is
method backup(editor) is
// Every field must be public for this to compile at all.
return [editor.text, editor.curX, editor.curY, editor.selectionWidth]
 
method restore(editor, state) is
editor.text = state[0]
editor.curX = state[1]
editor.curY = state[2]
editor.selectionWidth = state[3]
 
// Add a "zoomLevel" field to Editor and this class silently saves stale
// snapshots until someone notices. Rename a field and it stops compiling.
// The editor's encapsulation is now a polite fiction.
// The editor snapshots itself. Nobody else can see inside.
class Editor is
private field text, curX, curY, selectionWidth
 
method createSnapshot(): Snapshot is
return new Snapshot(this, text, curX, curY, selectionWidth)
 
// The caretaker holds an opaque token and knows only when to use it:
command.makeBackup() // before the change
command.execute()
// ...user regrets it...
command.undo() // memento.restore() puts everything back
 
// Add a zoomLevel field? Touch two lines in Editor and Snapshot.
// The history stack never even notices.
// The originator owns state worth protecting. It exposes a way to snapshot
// itself, because it is the only object allowed to read its own privates.
class Editor is
private field text, curX, curY, selectionWidth
 
method setText(text) is
this.text = text
 
method setCursor(x, y) is
this.curX = x
this.curY = y
 
method setSelectionWidth(width) is
this.selectionWidth = width
 
// The snapshot is built here, inside the class that owns the data.
method createSnapshot(): Snapshot is
// The memento is immutable, so everything goes in via the constructor.
return new Snapshot(this, text, curX, curY, selectionWidth)
 
// The memento: a sealed jar of past state. No getters, no setters, nothing
// for a curious caretaker to poke at.
class Snapshot is
private field editor: Editor
private field text, curX, curY, selectionWidth
 
constructor Snapshot(editor, text, curX, curY, selectionWidth) is
this.editor = editor
this.text = text
this.curX = curX
this.curY = curY
this.selectionWidth = selectionWidth
 
// Because the memento remembers which editor made it, it can put the
// values back itself.
method restore() is
editor.setText(text)
editor.setCursor(curX, curY)
editor.setSelectionWidth(selectionWidth)
 
// A command makes a fine caretaker: it grabs a snapshot right before it
// mutates the originator, and replays it if the user changes their mind.
class Command is
private field backup: Snapshot
 
method makeBackup() is
backup = editor.createSnapshot()
 
method undo() is
if (backup != null)
backup.restore()
 
// The caretaker stack is what turns one snapshot into an undo history.
class History is
private field stack: array of Command
 
method push(command) is
command.makeBackup()
command.execute()
stack.push(command)
 
method undo() is
if (stack.isNotEmpty())
stack.pop().undo()

When to use it

  • You need undo, redo, or a history of states, and the state you must capture lives in private fields.
  • You need transactional rollback: take a snapshot, attempt a risky multi-step operation, and restore if any step fails.
  • Direct access to an object's fields or setters would violate its encapsulation, but something outside it still has to manage its history.

Pitfalls

  • RAM is the bill. Full state copies at high frequency will bury you. Batch small changes into one undoable unit, or store deltas instead of full states.
  • Lifecycle leaks. Caretakers must know when a memento is obsolete and release it, otherwise the history stack keeps dead objects alive forever.
  • Dynamic languages cannot enforce the seal. Python, JavaScript, and PHP let a determined caller reach into any object. The pattern still buys you clean coupling, but treat the privacy as a contract, not a guarantee.
  • Mementos holding external handles. A snapshot containing an open socket or file descriptor restores into a world that has moved on. Capture values, not connections.

Don't confuse it with

  • Prototype. Prototype clones the entire live object, secrets and all, and the clone is a fully functional peer. Memento produces a deliberately inert, unreadable snapshot. When the object is simple and self-contained, Prototype is the cheaper answer and the GoF book says so plainly.
  • Serialization. Serializing to JSON is a snapshot too, but it is the exact opposite move on encapsulation: the state is now text that anything can read and edit. Serialization is for crossing process boundaries; Memento is for keeping state inside one and hiding it while it waits.
  • Command. Command captures an operation, Memento captures state. In a real undo system they work together, with the command acting as caretaker for the snapshot it took just before executing.
  • Iterator. Pairing them is a known trick: a memento can capture an iteration position so you can roll a traversal back.

Check yourself

Question 1 of 5

Who is allowed to read the data inside a memento?