From grimoire
Captures and restores object state for undo/redo, rollback, or snapshot functionality without breaking encapsulation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/grimoire:apply-memento-patternThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Capture an object's internal state in an external object (memento) so the state can be restored later, without exposing internal details.
Capture an object's internal state in an external object (memento) so the state can be restored later, without exposing internal details.
Adopted by: Every text editor's undo system (VS Code, IntelliJ, Vim) — each undo
operation restores a memento of prior editor state. Database savepoints (SAVEPOINT in
SQL) are mementos stored by the transaction engine. Git commits are mementos of
filesystem state — each commit captures a snapshot restorable at any time. Redux
time-travel debugging (DevTools' "jump to state") replays memento snapshots.
Impact: GoF documents that without Memento, undo requires either breaking
encapsulation (the undo manager reads private fields) or duplicating all object state
externally. Memento lets the object produce its own opaque snapshot, maintaining
encapsulation while enabling undo.
Why best: The alternative — Command pattern with undo methods — requires every
command to implement a perfect inverse operation. For complex state, that is
error-prone and sometimes impossible (file content after a regex replace can't always
be inverted). Memento captures state directly, making restoration exact.
Sources: Gamma et al. (1994) pp. 283–291; Git internals documentation; Redux DevTools
from dataclasses import dataclass
@dataclass(frozen=True)
class EditorMemento:
text: str
cursor_position: int
# frozen=True: memento is immutable — protects the snapshot
Make the memento's internal structure opaque to the caretaker (undo history).
frozen=True (or a private constructor) enforces this.
save() and restore() to the originatorclass TextEditor:
def __init__(self):
self._text = ""
self._cursor = 0
def type(self, text: str) -> None:
self._text = self._text[:self._cursor] + text + self._text[self._cursor:]
self._cursor += len(text)
def save(self) -> EditorMemento:
return EditorMemento(self._text, self._cursor)
def restore(self, memento: EditorMemento) -> None:
self._text = memento.text
self._cursor = memento.cursor_position
@property
def content(self) -> str:
return self._text
The originator creates and restores its own mementos — it's the only one that knows what state needs capturing.
class UndoHistory:
def __init__(self, editor: TextEditor):
self._editor = editor
self._history: list[EditorMemento] = []
def save(self) -> None:
self._history.append(self._editor.save())
def undo(self) -> None:
if self._history:
self._editor.restore(self._history.pop())
The caretaker holds mementos but treats them as opaque objects — it never inspects or modifies their contents.
editor = TextEditor()
history = UndoHistory(editor)
history.save()
editor.type("Hello")
history.save()
editor.type(" World")
print(editor.content) # "Hello World"
history.undo()
print(editor.content) # "Hello"
history.undo()
print(editor.content) # ""
If storing full snapshots is expensive (large documents, game world state), store deltas:
@dataclass(frozen=True)
class IncrementalMemento:
operation: str # "insert" or "delete"
position: int
text: str
# reconstruct full state by replaying forward or backward
apply-command-pattern with undo() is simpler and uses less memory.Caretaker that reads memento internals. The caretaker should treat mementos as black boxes. If it reads memento.text to display a preview, it couples to the internal structure — which must then stay stable.
Mutable mementos. If a memento can be modified after capture, the "snapshot" is no longer reliable. Always use immutable (frozen) mementos.
Not saving before every mutation. If the caretaker misses a save() call, an intermediate state is lost from the undo chain. Consider making save() automatic in the originator's mutating methods.
npx claudepluginhub jeffreytse/grimoire --plugin grimoireImplements GoF Memento pattern in TypeScript for undo/redo functionality and state snapshots, preserving encapsulation. Useful for editors, games, or any system requiring time-travel state restoration.
Generates Memento design pattern in PHP 8.4 for undo/redo state capture and restoration, with originator, memento, caretaker, value objects, and unit tests.
Encapsulates requests as objects for queueing, logging, undo/redo, and transactional behavior. Use when direct method calls cannot be queued, logged, or undone.