Memento
Also known as: Snapshot
Purpose
Help the user decide whether Memento is appropriate, then design and apply it so the originator object owns snapshot creation and restoration — without leaking private state to any other class.
When To Use
- User asks to explain the Memento or Snapshot pattern.
- An undo/redo mechanism is needed and the object's state must be captured without exposing private fields.
- Transactional rollback is required: save state before a risky operation, restore on failure.
- An object's state history must be tracked externally (caretaker/history) without coupling that history to the originator's internals.
- Iteration position needs to be saved and restored (combined with Iterator).
Inputs
- The originator class and which fields constitute its restorable state.
- The granularity of snapshots: full state each time, or incremental/diff-based.
- The caretaker type: history stack (undo/redo), command objects, transaction manager, etc.
- Memory constraints (drives snapshot size strategy and history limits).
- Language support for nested classes, access modifiers, or serialization.
Workflow
- Clarify the goal. Confirm whether the user wants explanation, code review, or implementation/refactor. Confirm undo/redo, rollback, or point-in-time restore is the actual requirement.
- Check applicability. If the object is simple with only public state, Prototype (clone) may be a lighter alternative. Use Memento when encapsulation must be preserved and state includes private fields.
- Identify the Originator. Confirm which class owns the state to be snapshotted. It must be the one producing and consuming mementos — no outsider should read raw state from the memento.
- Design the Memento. Mirror the originator's restorable fields as immutable fields set only in the constructor. No setters. Expose only metadata (timestamp, label) to outsiders — never the raw state.
- Choose the encapsulation strategy.
- Nested class (Java/C#/C++): memento is private to originator, caretaker holds an opaque reference.
- Intermediate interface: caretaker holds
IMemento(metadata only); originator casts to concrete class. - Memento-holds-originator reference:
restore()lives on the memento; originator provides setters.
- Add
save()/createSnapshot()to the Originator. Returns a new Memento capturing current state. The originator passes its own fields directly. - Add
restore(memento)to the Originator (or on the Memento itself). Applies the captured state back to the originator's fields. - Implement the Caretaker. Manages when to save and when to restore. Typically a stack for undo/redo or a list for full history. The caretaker never reads memento contents — only stores and passes them back.
- Validate behavior. Confirm private fields are inaccessible to the caretaker. Confirm save → mutate → restore returns the originator to the exact prior state. Confirm multiple independent mementos coexist.
- Explain tradeoffs. Call out RAM usage for frequent snapshots, lifecycle management of old mementos, and the language-specific encapsulation caveats.
Decision Branches
- If the object is simple and has no complex private state or external references: Consider Prototype (clone) instead — lighter and sufficient for value-like objects.
- If undo requires both state restore AND operation reversal: Combine Memento with Command — Command performs/undoes the operation; Memento carries the state snapshot.
- If the snapshot is taken per-command (one memento per command object): Store the memento on the command object itself (command acts as caretaker).
- If only iteration position needs saving:
Combine with Iterator — Memento snapshots the iterator's
currentPosition. - If memory is tight: Consider diff-based snapshots (store only changes), capped history, or weak references for old mementos.
- If the language doesn't enforce access restrictions at runtime (PHP, Python, JS):
Document the convention clearly; rely on naming (
_private) or serialization to opaque types.
Output Contract
When responding, provide:
- A suitability verdict (Memento or a simpler alternative like Prototype).
- Named roles mapped to the user's domain (Originator, Memento, Caretaker).
- The chosen encapsulation strategy and why it fits the language.
- Originator
save()andrestore()method sketches. - Caretaker structure (stack, history list, or per-command storage).
- Memory management guidance if frequent snapshots are expected.
- Validation checklist.
Quality Checks
- Originator produces and consumes mementos — no other class reads raw state from memento.
- Memento is immutable: constructor-only initialization, no setters, no public state fields.
- Caretaker holds mementos but cannot read their internal state.
restore(memento)returns the originator to the exact state captured atsave()time.- Multiple mementos can coexist without interfering with each other.
- Old mementos are cleaned up (bounded history or explicit caretaker lifecycle management).
Common Mistakes To Prevent
- Exposing originator state via public memento fields (breaks encapsulation — defeats the pattern).
- Storing mementos with references to mutable objects instead of deep copies (snapshot becomes stale).
- Caretaker logic that reads or interprets memento contents (should treat mementos as opaque).
- Unbounded history growth consuming all available RAM.
- Assuming shallow copy is sufficient when originator state contains nested mutable objects.
- Using Memento when the state is trivially simple — Prototype/clone is cleaner there.
References
- Detailed guide: REFERENCE.md
- Worked examples: EXAMPLES.md