[← Design patterns index](README.md) # Memento **Category:** Behavioral | **Also known as: Snapshot** | **Source:** `patterns/memento/MementoPattern/` ## Problem You want undo. The obvious approach — some other object reads the target's fields and stores copies — only works if the target exposes its state publicly. That breaks encapsulation, and every later refactor of the target ripples out into whatever was copying its fields. ## Solution Let the state's owner (the **Originator**) produce its own opaque snapshot (the **Memento**) and restore itself from one later. A **Caretaker** holds mementos — in an undo stack — without being able to read or alter them. GoF calls this the **narrow vs. wide interface**: the caretaker sees a narrow interface (it can only hold and pass the memento along); the originator sees a wide one (full access to restore itself). In languages with nested classes — "C++, C#, and Java", as _Dive Into Design Patterns_ notes — the classic implementation nests the memento **inside** the originator, which is exactly what this project does. ## Participants (GoF names → this repo) - **Originator** → `Originator` (implements `IOriginator`) - **Memento** → `Originator.Memento`, a **private nested class** implementing `IMemento` - **Caretaker** → `Caretaker`, holding a `Stack` ```mermaid classDiagram class IMemento { <> declaresNothing_theNarrowInterface } class IOriginator { <> +GetMemento() IMemento +SetMemento(IMemento) } class Originator { -Guid _id +GetMemento() IMemento +SetMemento(IMemento) +UpdateId() } class Memento { <> +Guid Id } class Caretaker { -Stack~IMemento~ _mementos -IOriginator _originator +Save() +Undo() } IOriginator <|.. Originator Originator *-- Memento : nested, private IMemento <|.. Memento Caretaker --> IOriginator Caretaker --> IMemento : holds, cannot read ``` ## Code ```csharp // IMemento.cs — the narrow interface. It declares NOTHING, on purpose: // that is precisely what stops the Caretaker from reading the snapshot. public interface IMemento { } // IOriginator.cs public interface IOriginator { IMemento GetMemento(); void SetMemento(IMemento memento); } ``` ```csharp // Originator.cs — Console.WriteLine calls kept, since the verified output depends on them public class Originator : IOriginator { // Private + nested: only Originator can even name this type, let alone read .Id private class Memento : IMemento { public Guid Id; } private Guid _id; public Originator() { _id = Guid.NewGuid(); Console.WriteLine($"Starting ID: {_id}"); } public IMemento GetMemento() { Memento memento = new Memento(); memento.Id = _id; Console.WriteLine("Creating Memento..."); return memento; // handed out as IMemento — opaque to everyone else } public void SetMemento(IMemento memento) { if (!(memento is Memento)) // guard: reject foreign IMemento implementations { return; } Console.WriteLine("Restting ID..."); // [sic] typo is in the original _id = (memento as Memento).Id; // downcast gives the WIDE interface Console.WriteLine($"ID is reset to {_id}"); } public void UpdateId() { Console.WriteLine("Updating Id..."); _id = Guid.NewGuid(); Console.WriteLine($"Updated ID is now {_id}"); } } ``` ```csharp // Caretaker.cs — holds history, never inspects it public class Caretaker { // container that could hold memento of Originator object private readonly Stack _mementos = new(); private readonly IOriginator _originator; public Caretaker(IOriginator originator) { _originator = originator; } public void Save() { _mementos.Push(_originator.GetMemento()); } public void Undo() { _originator.SetMemento(_mementos.Pop()); } } ``` `Caretaker` cannot reach the `Guid`. It has no member that returns one, and `IMemento` declares nothing — the compiler enforces the encapsulation, not a convention. ```csharp // Program.cs — the walkthrough (prompts/ReadLine calls elided) Originator originator = new Originator(); Caretaker caretaker = new Caretaker(originator); caretaker.Save(); // stack: [id0] originator.UpdateId(); // → id1 (never saved!) originator.UpdateId(); // → id2 caretaker.Save(); // stack: [id0, id2] originator.UpdateId(); // → id3 caretaker.Undo(); // pop id2 → current = id2 (id3 and id1 are gone) originator.UpdateId(); // → id4 caretaker.Save(); // stack: [id0, id4] originator.UpdateId(); // → id5 caretaker.Undo(); // pop id4 → current = id4 caretaker.Undo(); // pop id0 → current = id0 (stack now EMPTY) ``` ## Output (verified — GUIDs differ every run) ``` Starting ID: cdd24bbf-eac8-40b4-ae9a-9b09db9b2d7c Creating Memento... Updating Id... Updated ID is now e9119540-d546-4c38-bd51-462f90df3e47 Updating Id... Updated ID is now fcc5d975-3811-4f13-84ab-2768136a83c7 Creating Memento... Updating Id... Updated ID is now 02bafbee-f3e7-46d9-a6be-c757f4378595 Restting ID... ID is reset to fcc5d975-3811-4f13-84ab-2768136a83c7 <- undo skipped 02bafbee Updating Id... Updated ID is now ca9f441d-cdf9-4ffa-9269-362291f33235 Creating Memento... Updating Id... Updated ID is now b8cfe4d8-de93-4fc9-839d-f63a0a12eb7b Restting ID... ID is reset to ca9f441d-cdf9-4ffa-9269-362291f33235 Restting ID... ID is reset to cdd24bbf-eac8-40b4-ae9a-9b09db9b2d7c <- back to the very first ID ``` The two lessons the run makes concrete: **undo returns to the last state that was explicitly `Save()`d, not the previous value** (`e9119540` was never saved, so nothing can ever get back to it); and a stack of mementos gives multi-level undo for free — the last two `Undo()` calls walk all the way back to the starting ID. ## GoF pseudocode (text editor, with Command as the caretaker) ``` class Editor is // Originator private field text, curX, curY, selectionWidth method createSnapshot(): Snapshot is // Memento is immutable: state passed once, via the constructor return new Snapshot(this, text, curX, curY, selectionWidth) class Snapshot is // Memento private field editor: Editor private field text, curX, curY, selectionWidth method restore() is // memento restores its own originator editor.setText(text) editor.setCursor(curX, curY) editor.setSelectionWidth(selectionWidth) class Command is // Caretaker private field backup: Snapshot method makeBackup() is backup = editor.createSnapshot() method undo() is if (backup != null) backup.restore() ``` This is the "even stricter encapsulation" variant: the memento links back to its originator and owns `restore()`, so the caretaker never calls a setter on the originator at all. The repo's version instead keeps restoration on the originator (`SetMemento`) — the "intermediate interface" variant. ## Applicability - You must snapshot (part of) an object's state so you can restore it later — undo, but equally transaction rollback on error. - Direct access to that state via getters/setters would expose implementation details and break encapsulation. ## Pros / Cons | Pros | Cons | | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | Snapshots without violating encapsulation | Can consume lots of RAM if snapshots are frequent or large | | Simplifies the Originator by moving history-keeping to the Caretaker | The caretaker must track the originator's lifecycle to destroy stale mementos | | Multi-level undo falls out of using a stack | Dynamic languages (PHP/Python/JS) can't truly guarantee the memento stays untouched — C# can | ## Seen in the wild (.NET) - `TransactionScope` rollback semantics. - EF Core's change tracker, which holds original values so `Reload()`/rollback can restore them. - The undo stack in any editor control. ## Related patterns - **Command + Memento** is the classic undo pairing: the command captures a memento immediately before mutating the target, and restores it on undo. - **Iterator** — a memento can capture iteration state, letting it roll back. - **Prototype** is a simpler substitute when the object is straightforward and holds no external resource links. --- **← Prev:** [Mediator](mediator.md) · [All patterns](README.md) · **Next →** [Observer](observer.md)