Memento
To support undo you let outside code copy the editor's private fields and write them back.
The editor itself creates a small memento holding its state, and can restore from one.
You need undo or save points
The picture from life
A save point in a video game. Before the boss fight you save; if you lose, you reload and everything (health, items, position) is exactly as it was. The save file is just a sealed envelope: the game can read it, the player does not edit its insides.
The problem
What goes wrong when you do not use it.
To support undo you let outside code copy the editor's private fields and write them back. Now every caller knows the editor's internals, and any change to those fields breaks all of them.
- calls / uses
- You need undo or save points
- You must not expose internals to take a snapshot
- Transactions that may roll back
The idea
How the pattern fixes it.
The editor itself creates a small memento holding its state, and can restore from one. A history object keeps the mementos in a stack but never looks inside them.
- has a / holds
- creates
Who is who
- Editor (originator)
- Has the state. save() makes a memento, restore(m) loads one.
- Memento
- An immutable snapshot of the state.
- History (caretaker)
- Keeps mementos in a stack; never opens them.
Build it in steps
- 1Decide what state must be saved.
- 2Let the originator build an immutable memento.
- 3Keep mementos in a history stack.
- 4On undo, pop and restore.
Watch it run
Step through the calls. Tap an arrow to jump to it.
The text is 'Hello'. Ask the editor for a snapshot.
save()The code
Python and Java. Try writing the classes yourself from the diagram first.
class Memento:
def __init__(self, text):
self._text = text # sealed: only the editor reads this
def text(self):
return self._text
class Editor:
def __init__(self):
self.text = ""
def save(self):
return Memento(self.text)
def restore(self, memento):
self.text = memento.text()
class History:
def __init__(self):
self.stack = []
def push(self, memento):
self.stack.append(memento)
def pop(self):
return self.stack.pop()
def demo():
editor, history = Editor(), History()
editor.text = "Hello"
history.push(editor.save())
editor.text += " World"
typed = editor.text
editor.restore(history.pop())
return typed, editor.textWhat you gain
- Undo without breaking encapsulation
- The history stays simple
What it costs
- Many or big snapshots eat memory
- For tiny changes, storing diffs (Command) is lighter
You have already met it in
- Editor undo, browser form restore
- Database savepoints and snapshots
- Git commits as snapshots of a tree
Often mixed up with
Make it stick
Build a drawing canvas that saves a snapshot before every stroke and supports multi-level undo.