Memento Pattern explained simply
You are working on a document, you make a mistake, and you hit Ctrl+Z. The editor rolls back to exactly where you were a moment ago. No questions asked.
Ever wondered what is going on under the hood? How does the editor remember what “before” looked like? That is the Memento Pattern in action – it captures an object’s internal state as a snapshot so you can restore it later, without exposing any of the object’s internal details.
What is the Memento Pattern?
The Memento Pattern is a behavioral design pattern that lets you:
- Save an object’s internal state at a specific point in time.
- Restore that state later without breaking encapsulation.
- Undo operations by rolling back to a previous snapshot.
The key insight is that the object itself creates the snapshot and the object itself knows how to restore from it. Nobody else needs to understand the internals. The snapshot is just an opaque token that gets passed around.
A Real-Life Analogy
Think about a video game save system.
You are deep into an RPG. You have been grinding for hours, your character is geared up, and you are about to face the final boss. Before you walk through that door, you save your game.
You fight the boss. You die. Badly.
But it does not matter. You reload your save, and everything is exactly how it was – your health, your inventory, your position on the map. The game captured a snapshot of your entire state, stored it somewhere safe, and handed it back to you when you needed it.
That is the Memento Pattern. The game character is the Originator (the object whose state matters). The save file is the Memento (the snapshot). And the save/load menu is the Caretaker (the thing that holds onto the snapshots but never peeks inside them).
Class Diagram
Here is a UML class diagram showing the three participants and how they interact.
<mxfile>
<diagram name="Memento Pattern">
<mxGraphModel dx="1100" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="700">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<!-- DocumentMemento (Memento) -->
<mxCell id="2" value="DocumentMemento
─────────────────
- content: String
- cursorPosition: int
- formatting: String
─────────────────
+ getContent(): String
+ getCursorPosition(): int
+ getFormatting(): String" style="shape=rectangle;whiteSpace=wrap;html=1;align=center;fontSize=12;fontFamily=monospace;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="400" y="30" width="300" height="150" as="geometry"/>
</mxCell>
<!-- Document (Originator) -->
<mxCell id="3" value="Document
─────────────────
- content: String
- cursorPosition: int
- formatting: String
─────────────────
+ write(text: String): void
+ moveCursor(pos: int): void
+ applyFormatting(fmt: String): void
+ save(): DocumentMemento
+ restore(m: DocumentMemento): void" style="shape=rectangle;whiteSpace=wrap;html=1;align=center;fontSize=12;fontFamily=monospace;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="50" y="280" width="340" height="180" as="geometry"/>
</mxCell>
<!-- VersionHistory (Caretaker) -->
<mxCell id="4" value="VersionHistory
─────────────────
- history: Stack<DocumentMemento>
─────────────────
+ save(doc: Document): void
+ undo(doc: Document): void" style="shape=rectangle;whiteSpace=wrap;html=1;align=center;fontSize=12;fontFamily=monospace;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
<mxGeometry x="500" y="300" width="340" height="130" as="geometry"/>
</mxCell>
<!-- Document creates DocumentMemento -->
<mxCell id="5" value="creates" style="endArrow=open;dashed=1;strokeColor=#82b366;" edge="1" source="3" target="2" parent="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<!-- VersionHistory stores DocumentMemento -->
<mxCell id="6" value="stores" style="endArrow=diamond;endFill=1;strokeColor=#d6b656;" edge="1" source="4" target="2" parent="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<!-- VersionHistory uses Document -->
<mxCell id="7" value="uses" style="endArrow=open;dashed=1;strokeColor=#999999;" edge="1" source="4" target="3" parent="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Java Example: Document Editor with Version History
Let’s build something concrete – a document editor that supports undo through snapshots.
First, the Memento. This is the snapshot itself. It is immutable – once created, nobody can tamper with it:
public final class DocumentMemento {
private final String content;
private final int cursorPosition;
private final String formatting;
public DocumentMemento(String content, int cursorPosition, String formatting) {
this.content = content;
this.cursorPosition = cursorPosition;
this.formatting = formatting;
}
public String getContent() { return content; }
public int getCursorPosition() { return cursorPosition; }
public String getFormatting() { return formatting; }
}
Nothing fancy. It just holds data. The final keyword on the class and the final fields ensure that once a snapshot is taken, it cannot be changed. That is important – you do not want someone accidentally mutating your save point.
Next, the Originator. This is the Document class – the object whose state we actually care about:
public class Document {
private String content;
private int cursorPosition;
private String formatting;
public Document() {
this.content = "";
this.cursorPosition = 0;
this.formatting = "plain";
}
public void write(String text) {
content += text;
cursorPosition = content.length();
}
public void moveCursor(int position) {
this.cursorPosition = Math.min(position, content.length());
}
public void applyFormatting(String formatting) {
this.formatting = formatting;
}
public DocumentMemento save() {
return new DocumentMemento(content, cursorPosition, formatting);
}
public void restore(DocumentMemento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
this.formatting = memento.getFormatting();
}
@Override
public String toString() {
return "Document{content='" + content + "', cursor=" + cursorPosition
+ ", formatting='" + formatting + "'}";
}
}
Notice two key methods. save() creates a new memento from the current state. restore() takes a memento and resets everything back. The document is the only one that knows how to pack and unpack its own state.
Finally, the Caretaker. The VersionHistory class holds onto mementos but never looks inside them:
import java.util.Stack;
public class VersionHistory {
private final Stack<DocumentMemento> history = new Stack<>();
public void save(Document doc) {
history.push(doc.save());
}
public void undo(Document doc) {
if (history.isEmpty()) {
System.out.println("Nothing to undo.");
return;
}
doc.restore(history.pop());
}
}
The caretaker just manages a stack. Push to save, pop to undo. It has no idea what a DocumentMemento contains – it just passes it back to the document when asked.
Now let’s wire it all together:
public class MainProgram {
public static void main(String[] args) {
Document doc = new Document();
VersionHistory history = new VersionHistory();
// Start typing
doc.write("Hello ");
doc.applyFormatting("bold");
System.out.println("After first edit: " + doc);
history.save(doc);
// Keep editing
doc.write("World");
doc.applyFormatting("italic");
System.out.println("After second edit: " + doc);
history.save(doc);
// Edit some more
doc.write("!!!");
doc.moveCursor(5);
doc.applyFormatting("underline");
System.out.println("After third edit: " + doc);
// Oops, undo that last round of changes
System.out.println("\n--- Undo ---");
history.undo(doc);
System.out.println("After first undo: " + doc);
// Undo again
System.out.println("\n--- Undo ---");
history.undo(doc);
System.out.println("After second undo: " + doc);
}
}
Output:
After first edit: Document{content='Hello ', cursor=6, formatting='bold'}
After second edit: Document{content='Hello World', cursor=11, formatting='italic'}
After third edit: Document{content='Hello World!!!', cursor=5, formatting='underline'}
--- Undo ---
After first undo: Document{content='Hello World', cursor=11, formatting='italic'}
--- Undo ---
After second undo: Document{content='Hello ', cursor=6, formatting='bold'}
Each undo pops the most recent snapshot and restores the entire document state – content, cursor position, and formatting – all at once. The document goes back in time, exactly to where it was when we saved.
Key Components
Let’s break down the three roles:
- Memento (
DocumentMemento): An immutable snapshot of the originator’s state. It stores only what is needed and exposes nothing that would let outsiders modify the originator directly. - Originator (
Document): The object whose state you want to save and restore. It is the only one that knows how to create a memento from its current state and how to restore itself from a memento. - Caretaker (
VersionHistory): Manages the collection of mementos. It requests saves, holds onto snapshots, and hands them back when an undo is needed. It never inspects or modifies the memento’s contents.
When to Use the Memento Pattern
- You need undo/redo functionality.
- You want to create checkpoints or save points that you can roll back to.
- You need to preserve encapsulation – the state should be saved and restored without exposing the object’s internals.
- You are implementing transactional behavior where a failed operation should roll back to a known good state.
Advantages and Disadvantages
Advantages:
- Preserves encapsulation. The originator’s internal structure stays hidden.
- Simplifies the originator. It does not need to manage its own version history.
- Easy to implement undo/redo with a simple stack.
- Snapshots are independent of each other, so you can jump to any saved state.
Disadvantages:
- Can consume significant memory if the originator’s state is large and you save frequently.
- The caretaker has no way to know how much state a memento holds, so it cannot optimize storage.
- In languages without good encapsulation mechanisms, it can be hard to truly keep the memento opaque.
- If the originator’s state includes references to other objects, you might need deep copies to avoid shared mutable state.
Memento vs Command Pattern
These two patterns are often mentioned together because both can enable undo functionality, but they approach it differently.
The Command Pattern records operations. Each command knows how to execute an action and how to reverse it. Undo means running the inverse operation. This works well when operations are simple and reversible – like “move 10 pixels right” can be undone by “move 10 pixels left.”
The Memento Pattern records state snapshots. Instead of reversing an operation, you just restore a previous snapshot. This is better when operations are complex or not easily reversible – like a series of text edits that interact with each other.
In practice, some systems use both. A text editor might use commands for simple keystrokes but take full snapshots at certain intervals as a safety net.
Real-World Use Cases
- Ctrl+Z in text editors: Every editor from Notepad to VS Code uses some form of state snapshots to power undo.
- Database transaction rollback: Before a transaction begins, the database saves enough state to roll back if something goes wrong. That is a memento.
- Browser back button: The browser keeps a history of page states. Hitting back restores a previous state rather than re-navigating.
- Game save systems: As we discussed – save your progress, try something risky, reload if it goes sideways.
- Java Serialization:
java.io.Serializableis essentially a mechanism for capturing an object’s complete state as a byte stream, which is the memento concept at its most literal. - Git commits: Each commit is a snapshot of your entire project state. You can check out any previous commit to restore the codebase to that point in time.
Wrapping Up
The Memento Pattern is one of those patterns that clicks immediately once you see it. You have been using it your whole life – every time you hit undo, load a save file, or roll back a transaction.
The core idea is dead simple: before you do something risky, take a snapshot. If things go wrong, restore the snapshot. The originator knows its own state, the memento holds the snapshot, and the caretaker manages the timeline. Everyone has a clear job, and nobody needs to know anyone else’s internals.
It is not the right choice for every situation – if your objects are huge or you need to save thousands of snapshots, the memory cost adds up fast. But for undo/redo systems, transactional safety nets, and checkpoint-based recovery, it is hard to beat.