Behavioral Pattern Pattern Deep Dive

Memento

Every text editor's Ctrl+Z has to answer an awkward question: how do you restore an object to exactly how it looked a minute ago, when most of what makes it "how it looked" is private state you were never supposed to reach into from outside? The Memento pattern solves this by letting the object snapshot and restore its own state, handing out an opaque token that an external caretaker can store and hand back later — without ever peeking inside it.

⚠️ The Problem

Say you're building undo for a text editor. An Editor object tracks the current text, the cursor position, and the selected font — all private fields, because nothing outside the editor should be able to mutate them directly. But an UndoManager needs to be able to save "the state right now" and restore it later. The naive fix is to make all those fields public, or add getters/setters for every single one, so UndoManager can copy them out into its own history stack. Now the editor's internals are exposed to any code that wants to poke at them, encapsulation is gone, and every time you add a new piece of editor state you have to remember to also wire it into the undo copying logic somewhere else entirely.

✅ The Solution

Let the Editor create its own Memento — a small snapshot object containing a copy of its state — via a method like save(), and let it restore itself from one via restore(memento). The Memento's contents can be totally opaque to everyone except the Editor that created it. An external Caretaker (here, a History stack) stores these Mementos in order and hands them back on request, but never reads or modifies what's inside one — it just treats each Memento as a black box. The Editor keeps full control over what "its state" even means; the Caretaker just manages a timeline.

Example 1 — a text editor's undo history

Primary Example — JavaScript
class EditorMemento {
  #text;
  #cursor;

  constructor(text, cursor) {
    this.#text = text;
    this.#cursor = cursor;
  }

  // Only the Editor class is expected to call these — the Caretaker
  // never touches them, it just holds the object.
  getText() { return this.#text; }
  getCursor() { return this.#cursor; }
}

class Editor {
  #text = "";
  #cursor = 0;

  type(str) {
    this.#text += str;
    this.#cursor += str.length;
  }

  save() {
    return new EditorMemento(this.#text, this.#cursor);
  }

  restore(memento) {
    this.#text = memento.getText();
    this.#cursor = memento.getCursor();
  }

  get content() { return this.#text; }
}

class History {
  #stack = [];

  push(memento) {
    this.#stack.push(memento);
  }

  pop() {
    return this.#stack.pop();
  }
}

const editor = new Editor();
const history = new History();

history.push(editor.save());   // snapshot: ""
editor.type("Hello");
history.push(editor.save());   // snapshot: "Hello"
editor.type(", world!");
console.log(editor.content);   // "Hello, world!"

editor.restore(history.pop()); // back to "Hello"
console.log(editor.content);   // "Hello"

The History caretaker never once looks at #text or #cursor — it just pushes and pops opaque EditorMemento objects. All the knowledge of what "editor state" consists of stays inside Editor, where it belongs.

Example 2 — a secondary context: game character save points

Secondary Example — C#
public class CharacterMemento
{
    public int Health { get; }
    public int Level { get; }
    public string Location { get; }

    public CharacterMemento(int health, int level, string location)
    {
        Health = health;
        Level = level;
        Location = location;
    }
}

public class Character
{
    private int _health = 100;
    private int _level = 1;
    private string _location = "Starting Village";

    public void TakeDamage(int amount) => _health -= amount;
    public void LevelUp() => _level++;
    public void MoveTo(string place) => _location = place;

    public CharacterMemento SavePoint()
        => new CharacterMemento(_health, _level, _location);

    public void LoadSavePoint(CharacterMemento memento)
    {
        _health = memento.Health;
        _level = memento.Level;
        _location = memento.Location;
    }

    public override string ToString()
        => $"HP:{_health} Lvl:{_level} @ {_location}";
}

public class SaveSlotManager
{
    private readonly Dictionary<string, CharacterMemento> _slots = new();

    public void Save(string slotName, CharacterMemento memento) => _slots[slotName] = memento;
    public CharacterMemento Load(string slotName) => _slots[slotName];
}

// Usage
var hero = new Character();
var slots = new SaveSlotManager();

hero.MoveTo("Dark Forest");
hero.TakeDamage(60);
slots.Save("before-boss-fight", hero.SavePoint());

hero.TakeDamage(50); // hero dies to the boss
Console.WriteLine(hero); // HP:-10 Lvl:1 @ Dark Forest

hero.LoadSavePoint(slots.Load("before-boss-fight"));
Console.WriteLine(hero); // HP:40 Lvl:1 @ Dark Forest

SaveSlotManager plays the same caretaker role as History did above — it just files Mementos under named slots instead of a stack, which is all a "save game" system really is.

👍 When To Use It

  • You need undo/redo, checkpoints, or save points, and the object's state is meant to stay encapsulated rather than exposed via public getters/setters.
  • Computing the reverse of an operation directly would be awkward or impossible, so it's easier to just capture "the whole state" before the operation and restore it wholesale.
  • You want the history-keeping logic (the caretaker) to be a completely separate concern from the object whose history is being kept.

👎 When NOT To Use It

  • When the object's state is large and snapshots are taken frequently — copying the full state on every change can get expensive in memory unless you add sharing/diffing tricks.
  • When reversing an operation is simple to express directly (e.g. an "insert" is trivially undone by a matching "delete") — a Command with its own undo() is often lighter weight than snapshotting everything.
  • When there's no real need for history at all — a single "are you sure?" confirmation is often simpler than building a whole snapshot/restore mechanism.

❓ FAQ

How is Memento different from Command's undo()?

They solve overlapping problems differently. A Command with an undo() method typically knows how to reverse the one specific operation it represents — an "insert text" command undoes itself by deleting exactly the text it inserted. Memento instead captures the object's entire state before the change and restores that whole snapshot afterward, regardless of what operation happened. Memento is the better fit when computing a precise inverse is hard or when many different kinds of changes all need to share one uniform undo mechanism; Command's approach is lighter when each operation's reverse is easy to state directly.

Doesn't this break encapsulation anyway, since the Memento holds the object's state?

The state still leaves the object, but who can read it doesn't. The Memento's contents are only meaningful to the class that created it — in the JavaScript example above, getText()/getCursor() are effectively a "narrow" interface intended for Editor alone; the History caretaker never calls them. In languages with stricter access control, the Memento's accessors can be made genuinely private to the originator class, so the caretaker really can only push, pop, and hold — not read.

Can a Memento store just the diff instead of the full state?

Yes — nothing in the pattern requires a full copy. Storing just what changed (a diff, or a command that can regenerate the prior value) is a common optimization once full snapshots get too expensive, especially for large objects or documents. The core idea — an opaque object that lets the originator restore a prior state, managed by a caretaker that never inspects it — stays the same either way.

🙋 There Are No Dumb Questions

Q: Isn't a Memento basically just a backup copy of an object?
A: Conceptually, yes — but the pattern is specifically about who creates and reads it. A plain backup copy is often built by outside code reaching into the object's fields. A Memento is built by the object itself and deliberately kept opaque to outsiders, which is what keeps the object's internal representation free to change later without breaking any code that stores its history.

✅ Quick Check

In the Memento pattern, what is the Caretaker (like History or SaveSlotManager) allowed to do with a Memento?


Next up: what happens when an object needs to behave completely differently depending on its current state, without a wall of if/else checks scattered everywhere? That's the State pattern.

← Back to
Next →