Home Design Patterns Memento
Memento
Behavioral Gang of Four“Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.”
Gamma, Helm, Johnson & Vlissides, p. 283Purpose
A Memento is a snapshot of an object's state, handed out as a token that nobody else can read. Somebody holds it, and later gives it back, and the object puts itself into the state it was in.
The four words that matter are "without violating encapsulation". Capturing state is trivial if you are willing to make it public, and that is exactly what this pattern refuses to do. If the undo stack can read the fields it saved, so can everything else, and the class has lost the private state that justified its existence. A Memento is the arrangement that lets state leave an object without becoming visible.
Design
Three roles. The Originator creates a memento holding a copy of whatever it needs to restore itself, and knows how to accept one back. The Memento holds that state. The Caretaker keeps mementos, usually in a stack, and hands them back on request, and is not allowed to look inside.
That restriction is meant to be enforced by two interfaces on one class: a
wide one for the Originator and a narrow one, usually empty, for everybody else.
C++ does it with friend. Java does it with a package-private inner
class. C# does it with internal members, or with a private nested
type exposed through an empty public interface. None of these is airtight, and
that is worth knowing before you rely on it.
Example
Every Autobot Robotics arm carries a set of tuning parameters: servo gains per axis, tool center point offsets, backlash compensation, camera-to-arm calibration. They are adjusted on site, usually by one engineer, usually at night, usually because something is not quite right. Getting them wrong ranges from a scrapped part to an arm striking its own fixture.
The old procedure was a printed sheet. Write down the current values, change one, and if it is worse, type the old ones back in. That fails in the obvious way: at three in the morning, after the fourth change, the sheet does not match the machine, and nobody is certain which values were the good ones.
The controller now takes a snapshot before every parameter change. A
TuningSession keeps a stack of them for the shift and can roll back
any number of steps, and the snapshot is written to the controller's flash so it
survives a restart. Critically, TuningSession cannot read what is in
a snapshot. It does not know what a servo gain is, so it never has to be updated
when a new parameter is added, and there is no chance of it displaying a stale
value as if it were live. The one thing the team documented loudly is that
rolling back the parameters does not roll back the world: the part that was
machined with the bad gains is still bad.
class ParameterSnapshot // the Memento
{
private ServoGains gains;
private ToolOffset offset;
internal ParameterSnapshot(ServoGains g, ToolOffset o) {
gains = g; offset = o;
}
internal ServoGains getGains() { return gains; }
internal ToolOffset getOffset() { return offset; }
}
class Controller // the Originator
{
private ServoGains gains;
private ToolOffset offset;
public ParameterSnapshot capture() {
return new ParameterSnapshot(gains.copy(), offset.copy());
}
public void restore(ParameterSnapshot s) {
gains = s.getGains();
offset = s.getOffset();
}
}
class TuningSession // the Caretaker
{
private Controller controller;
private Stack<ParameterSnapshot> history;
public void beforeChange() {
history.Push(controller.capture());
}
public void rollBack() {
if (history.Count() > 0) {
controller.restore(history.Pop());
}
}
}
internal, so TuningSession can hold a snapshot it
cannot read. And capture() calls copy(), because a
snapshot that shares a mutable object with the live state is not a
snapshot.Seen in the Wild
PostgreSQL's SAVEPOINT and ROLLBACK TO are exactly
this contract at the database level: you get back a named handle whose contents
mean nothing to you, and later you hand it in to be restored. Filesystem and
hypervisor snapshots in ZFS, btrfs and VMware are the same shape at yet another
scale. In .NET, IEditableObject with its
BeginEdit, CancelEdit and EndEdit methods
is the pattern written into a framework interface, with the object holding its
own memento so a data grid can cancel a row edit without knowing what a row
contains.
Comparisons
- Command is the usual partner. A command that cannot compute its own inverse carries a memento of what the receiver looked like before it ran. Together they are how undo actually gets built.
- Prototype looks similar because both copy an object. A clone is a fully usable object of the same type; a memento is deliberately unusable by anyone but its originator.
- Iterator can use one to save and restore a position in a traversal without revealing what a position is made of.
- Private Class Data is the same instinct applied continuously rather than at a moment: both are about deciding who is permitted to see state.
Why It Exists
My take
Memento exists to resolve a direct conflict between two things we normally treat as unambiguously good. Encapsulation says an object's state is nobody else's business. Undo, checkpointing and rollback say somebody else has to be able to put the state back. Without the pattern, the practical resolution is always the same and always bad: someone adds getters for everything so the undo system can work, and six months later four unrelated classes are reading those getters and the state is public in everything but name.
What the pattern contributes is the idea of an opaque token. The holder gets custody without comprehension. Once you have seen it, you notice that this is how all the durable versions of the idea work. A database savepoint is a name you cannot inspect. A filesystem snapshot is a handle whose internals belong to the filesystem. Nobody exposes the structure, because the moment you do, the format is frozen and every future change to the internals becomes a compatibility problem.
The thing I would warn about is that a memento restores your program's idea of state, and programs that touch the world have another kind of state that does not roll back. The tuning parameters go back; the scrapped part does not. The row in the ledger goes back; the email does not. I have watched people build a careful undo stack and then quietly assume it covered more than it did, which is a worse position than having no undo at all, because it is trusted. Be explicit about the boundary of what a snapshot owns.
Criticisms
The cost is memory, and it is not always small. A full snapshot per change
means a document editor holding many copies of a document, and the usual
mitigation, storing deltas instead of states, is a substantially harder design
that the pattern gives you no help with. It also makes the cost invisible at the
call site: beforeChange() looks free and may copy a megabyte.
Shallow copying is where this actually goes wrong in practice. A memento that stores a reference to a mutable object has captured nothing, because the live object keeps mutating the same instance the snapshot points at. The bug does not appear until somebody rolls back and gets the current values, and by then the snapshot has been trusted for months. Every field in a memento needs a deliberate decision about depth, and nothing in the type system prompts you to make it.
Finally, the encapsulation promise is weaker than it sounds. C++ has
friend; everyone else has a language-level approximation that
reflection, serialization or a determined colleague in the same assembly can walk
straight through. And restoring is not transactional: between the snapshot and
the rollback, other objects may have read the state and acted on it, and putting
the originator back does nothing about what they did with what they saw.