Behavioral · Gang of Four
Memento
Memento is a behavioral pattern for saving and restoring internal state without exposing private fields. The originator creates a sealed snapshot; the caretaker stores snapshots and never peeks inside.
Example: an editor has private text, a cursor, and a selection. Undo should not mean getText() for the whole world — then every toolbar can poke the insides. The editor creates a sealed only it can read. The undo stack holds those snapshots.
How to explain this in an interview
- Chess move with a clear inverse: Command. Castling / en passant mess: Board.save() first.
- Editor keystrokes: Memento. Do not invert every character as a command.
- Public getters on the snapshot mean you shipped a DTO, not a memento.
- Bound the stack. Snapshots cost memory; say that if the document is huge.
Originator
Editor / Board
Creates and applies. The only type allowed to read the .
Memento
Sealed envelope
Nested class, private fields. to everyone but the originator.
Caretaker
History stack
The pushes and pops. It never calls getText().
Command vs Memento
Store the verb, or store the picture?
Command stores an action. Memento stores a picture of state. Undo a chess move with Command when PlayMove can invert: put the piece back. Undo an editor with Memento when inverting every keystroke is silly: restore the last picture. Combining them is normal — execute takes a memento first, undo restores it — but they answer different questions.
| Question | Command | Memento |
|---|---|---|
| What is stored? | The verb + enough to reverse | A sealed picture of fields |
| Who knows internals? | Nobody — public API of the receiver | Only the originator |
| Chess undo | PlayMove.undo() if inverse is clear | restore(snapshot) if reversing is a mess |
| Editor undo | Usually the wrong tool | The default: save before type() |
If you find yourself copying private fields in the command, you wanted a memento the originator created. See encapsulation.
Java: undo without getters for every field
History cannot read m.text. Nested Memento keeps the fields private. That is the whole trick, in Java.
class Editor {
private String text = "";
private int cursor = 0;
Memento save() {
return new Memento(text, cursor);
}
void restore(Memento m) {
this.text = m.text;
this.cursor = m.cursor;
}
void type(String s) {
text = text.substring(0, cursor) + s + text.substring(cursor);
cursor += s.length();
}
static final class Memento {
private final String text;
private final int cursor;
private Memento(String text, int cursor) {
this.text = text;
this.cursor = cursor;
}
}
}
class History {
private final Deque<Editor.Memento> stack = new ArrayDeque<>();
void push(Editor.Memento m) { stack.push(m); }
Editor.Memento pop() { return stack.pop(); }
// History cannot read m.text — the fields are private to Editor.
}Snapshots cost memory
For a board, a copy of the grid is fine. For a huge document, diffs are an optimization, not a different pattern. Bound the stack. Do not keep every keystroke forever in a machine-coding round unless they ask.
Combine without mixing jobs
execute() may call editor.save() and push the memento. undo() restores. The command still does not open the envelope. The originator still does not know about the stack.
A memento with public getters
If anyone can call memento.getText(), you did not protect the insides. You published a DTO.
Undo by poking private fields
command.board.grid = oldGrid is not Memento and not encapsulation. Reverse through the API, or let the board save and restore a snapshot it owns.
Save before mutate
The caretaker’s rule: push a snapshot, then change the originator. Undo pops and restore. Redo is a second stack of mementos, not a flag on Editor. History still cannot read fields.
1 · save
Editor.save() returns an opaque Memento.
2 · type
Editor.type mutates text and cursor.
3 · restore
History.pop() → Editor.restore(m).
Java: command that does not open the envelope
Combining patterns: the command holds a memento. It still does not read text. Chess uses this when PlayMove cannot invert castling.
class TypeText implements EditCommand {
private final Editor editor;
private final String chunk;
private Editor.Memento before;
public void execute() {
before = editor.save();
editor.type(chunk);
}
public void undo() {
editor.restore(before);
}
}Checkpoints vs keystroke undo
A game save is a named memento. An editor undo stack is many small ones. Same pattern, different caretaker policy. Cap the undo stack unless the interviewer asks for unlimited history.
Package-private is not a leak
Nested private fields are the Java idiom. A public record Memento(String text, int cursor) is a DTO. Do not ship that as “Memento.”
How to explain this in an interview
“History holds snapshots it cannot read. Editor.save / restore is the only API.” If undo is ‘put the piece back,’ that is Command. If undo is ‘the board looked like this,’ that is Memento. Public getters on the snapshot mean you shipped a DTO.
Nearby in LLD