StudyDSA logoStudyDSA

Command Palette

Search for a command to run...

Sign InSign Up
Sign Up

Data StructuresAlgorithmsBig-O NotationDesign PatternsSystem DesignMachine LearningPhysicsRoboticsAI Research

Factory MethodAbstract FactoryBuilderPrototypeSingletonAdapterBridgeCompositeDecoratorFacadeFlyweightProxyChain of ResponsibilityCommandIteratorMediatorMementoObserverStateStrategyTemplate MethodVisitor

Definition
StudyDSA

Where complexity meets clarity.
By Armas Zarra.

Topics

  • Data Structures
  • Algorithms
  • Big-O Notation
  • Robotics
  • AI Research
  • Machine Learning

Practice

  • Blind 75
  • LeetCode 75
  • NeetCode 150

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Armas Films LLC

Design PatternsMemento

Memento

A behavioral pattern that captures an object's state in a sealed snapshot only the object itself can read back, which is how undo works without leaking internals.

Definition

Undo needs yesterday's state, and the obvious implementation, a history class that reads and rewrites the editor's fields, quietly hands your object's insides to an outsider. Memento is the Gang of Four behavioral pattern that captures an object's state in a snapshot only that object can read back, what the standard reference frames as saving and restoring previous state without revealing the details of its implementation.


A memento is really just a sealed envelope: the editor writes its state inside, anyone may hold or stack the envelope, and only the editor can open it. That opacity is the entire pattern. Plenty of code can take snapshots, but a snapshot the whole program can read into is a leak wearing a feature's name, and the envelope is what separates this pattern from a public struct named Backup.

The History That Reads Your Fields

Here's the obvious implementation and its bill. The editor's fields go public so the history can copy them out and write them back, and the closing comment tallies what that costs:

Two couplings just shipped. The history now mirrors the editor's exact field layout, so every rename, added field, or type change in the editor breaks a class whose job was supposed to be stacking. And the door swings both ways: fields opened for the history's reading are open to everything, writable by anything holding a reference. The undo feature made the editor's privacy a casualty.

Save, Mutate, Restore

The memento version seals the state. Editor.save() returns a Snapshot whose fields nothing else can read, restore() accepts one back, and History stores them blind. The example types a, checkpoints, types b and c, then restores. Before reading past the code, predict the two printed lines, text and cursor both:

It prints abc 3 and then a 1: one restore jumped the editor back past two edits, because the snapshot stored the whole state at the checkpoint, not a diff. Cursor and text traveled together, which is why the restored editor is consistent rather than a text of a with a cursor stranded at position 3. Check the Java tab for the pattern's sharpest trick: Snapshot as a nested class with private fields means the compiler itself enforces that only Editor reads inside the envelope.

Who Does What

Three roles with a strict information hierarchy. The Originator (Editor) produces snapshots of its own state and accepts them back, the only role that sees inside. The Memento (Snapshot) is an immutable value, loaded once via its constructor. The Caretaker (History) decides when to save and when to restore, holding mementos it cannot inspect, competent entirely about timing and never about contents.


The pattern works like game save files: the game writes saves in its own format, while the launcher that lists, slots, and deletes them never understands a byte of what's inside. The analogy exposes the pattern's nearest confusion. Save files are serialization, built to outlive the process and be read by any code that learns the format, while a memento is in-memory and deliberately readable by one class only. Serialize your state to JSON and anything can parse it back, which is precisely the open door this pattern exists to close.

Memento: Step-by-Step

Installing the pattern is four moves:

1. Seal the State in a Constructor-Only Object

The Snapshot takes its data at construction and exposes nothing afterward: no getters, no setters, just a sealed value. Immutability here is what makes a year-old snapshot as trustworthy as a fresh one.

2. Let Only the Originator Open It

save() and restore() live on the editor, the one class that understands its own field layout. Java and C# enforce this with nested classes, Go and Rust with unexported fields, and Python by convention.

3. Hand the Caretaker Opaque Boxes

History pushes and pops snapshots without reading a single field inside one. Its whole competence is when to save and when to restore, which survives any change to what a snapshot contains.

4. Decide What a Snapshot Includes

The example saves text and cursor together because restoring one without the other leaves a lying editor. A snapshot's contents are a contract: everything needed for a consistent restore, and nothing that belongs to a different object's story.
Snapshots or Inverses

Undo has exactly two engineering strategies, and this page is the second one's home. Storing inverse operations is Command's approach: memory-light, replay-friendly, and demanding that every action know how to reverse itself. Storing prior state is Memento's: simple and universal, since nothing needs an inverse when you can overwrite wholesale, and paid for in memory. The restore in the example jumped two edits in one hop, something inverse-based undo would replay step by step.


The memory bill is the pattern's documented critique, with the standard reference warning that apps consume serious RAM when clients snapshot too often. Full copies of a large document per keystroke is the canonical mistake. The same reference points at the practical synthesis: Command and Memento work together, commands carrying the cheap reversible edits and mementos checkpointing around operations too gnarly to invert, which is roughly how every serious editor you've used actually does it.

The Go and Rust Versions

Go makes snapshots almost suspiciously easy: assigning a struct copies it, so Save() returning a Snapshot by value is a complete implementation, and unexported fields seal the envelope at the package boundary. The honesty tax arrives with reference fields, since slices and maps copy as shared headers, the same shallow trap Prototype documents, and deep copies of those are handwritten.


Rust hands the pattern two gifts. derive(Clone) writes the snapshot copying, deep for owned data, so save() is a clone and restore() is an assignment. And the borrow checker enforces the caretaker's blindness structurally: the history owns its snapshots outright, holds no live references into the editor, and couldn't mutate the originator through a stored memento if it tried. The encapsulation guarantee other languages maintain by discipline arrives in Rust as a compile error.

Best Practices

The pattern is a sealed box and a stack, and these five keep the contents trustworthy:


A memento with public getters is just a struct, and every caretaker becomes a reader of the originator's internals. Use the language's strongest privacy tool available, nested classes where they exist, module boundaries where they don't.

Check Yourself

Two questions before you go. First: the history stored and returned the snapshot that fixed the editor, yet it can't read one field inside it. What does that opacity protect, in both directions? Second: snapshot-based undo and inverse-based undo each pay a different cost and risk a different failure. Name both pairs. Both answers are on this page.


Then go spot it in the wild. Every game save slot is a memento managed by a launcher that can't parse it, database savepoints bookmark transaction state for a rollback that restores wholesale, and VM snapshots do it to entire operating systems. Last, look at whatever undo feature lives in an app you maintain, or the spot where users keep asking for one, and run the strategy question from this page against its data: cheap to invert, or cheap to copy? The answer picks your pattern.

the_exposed_state.py

class Editor:
    def __init__(self):
        self.text = ""    # public, because History needs to read it
        self.cursor = 0   # public, same reason

    def type(self, chars: str) -> None:
        self.text += chars
        self.cursor += len(chars)

class History:
    def __init__(self):
        self._states: list[tuple[str, int]] = []

    def save(self, editor: Editor) -> None:
        # History reaches into the editor's fields directly
        self._states.append((editor.text, editor.cursor))

    def undo(self, editor: Editor) -> None:
        # ...and writes them back directly, too
        text, cursor = self._states.pop()
        editor.text = text
        editor.cursor = cursor

# History now knows the editor's exact field layout. Rename a
# field, add one, or change a type, and History breaks. Worse,
# anything holding a History can rewrite the editor at will.

memento.py

class Snapshot:  # Memento: a sealed envelope, constructor-only
    def __init__(self, text: str, cursor: int):
        self._text = text
        self._cursor = cursor

class Editor:  # Originator: the only reader and writer of snapshots
    def __init__(self):
        self.text = ""
        self.cursor = 0

    def type(self, chars: str) -> None:
        self.text += chars
        self.cursor += len(chars)

    def save(self) -> Snapshot:
        return Snapshot(self.text, self.cursor)

    def restore(self, snapshot: Snapshot) -> None:
        self.text = snapshot._text
        self.cursor = snapshot._cursor

class History:  # Caretaker: stores envelopes, never opens one
    def __init__(self):
        self._snapshots: list[Snapshot] = []

    def push(self, snapshot: Snapshot) -> None:
        self._snapshots.append(snapshot)

    def pop(self) -> Snapshot:
        return self._snapshots.pop()

# Example usage
editor = Editor()
history = History()

editor.type("a")
history.push(editor.save())  # checkpoint at "a"
editor.type("b")
editor.type("c")
print(editor.text, editor.cursor)  # abc 3

editor.restore(history.pop())
print(editor.text, editor.cursor)  # a 1