One-line Definition
Captures and externalizes an object's internal state so it can be restored later, all without violating encapsulation.
Problem
Suppose you are building a text editor and want to add an "Undo" feature. To save the previous state, you might expose the editor's internal fields (text, cursor position, scroll offset) via public getters, save them in an external history manager, and write them back later. However, making internal fields public violates encapsulation and makes the editor's internal structure impossible to change safely. The Memento Pattern solves this by letting the editor itself create a private "snapshot" (memento) that external objects can store but cannot read or alter.
Real-world Analogy
Think of a video game save point. When you save, the game captures your exact state — position, health, inventory, quest progress — into a save file. If you die, you load the save and everything is restored exactly. The save file is the memento — it holds your state without exposing the game's internal data structures to you.
Structure
- Client / Caretaker
- Manages the memento history (e.g., a stack). Requests mementos from the Originator, but never examines or modifies their contents.
- Originator
- The object whose state needs to be saved and restored. It exclusively creates and reads mementos.
- Memento
- An opaque snapshot of the Originator's state.
Diagram
classDiagram
class Caretaker {
-List~Memento~ history
+undo()
}
class Originator {
-state
+save(): Memento
+restore(Memento)
}
class Memento {
-state
+getState()
}
Caretaker o-- Memento : stores
Caretaker --> Originator : requests save/restore
Originator ..> Memento : creates/reads
Code Walkthrough
Notice how the Memento class is heavily protected. The History (caretaker) can store Memento objects in a stack, but it has absolutely no way to access the content or cursorPosition inside them.
class TextEditor {
private String content = "";
private int cursorPosition = 0;
public void write(String text) {
content += text;
cursorPosition = content.length();
System.out.println("Content: \"" + content + "\" | Cursor: " + cursorPosition);
}
public Memento save() {
return new Memento(content, cursorPosition);
}
public void restore(Memento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
System.out.println("Restored: \"" + content + "\" | Cursor: " + cursorPosition);
}
// Static nested class restricts access to the Memento's internals
static class Memento {
private final String content;
private final int cursorPosition;
private Memento(String content, int cursorPosition) {
this.content = content;
this.cursorPosition = cursorPosition;
}
private String getContent() { return content; }
private int getCursorPosition() { return cursorPosition; }
}
}
class History {
private final Stack<TextEditor.Memento> undoStack = new Stack<>();
public void save(TextEditor editor) {
undoStack.push(editor.save());
}
public void undo(TextEditor editor) {
if (!undoStack.isEmpty()) {
editor.restore(undoStack.pop());
} else {
System.out.println("Nothing to undo");
}
}
}
class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();
history.save(editor);
editor.write("Hello ");
history.save(editor);
editor.write("World");
history.save(editor);
editor.write("!!!");
System.out.println("--- Undo ---");
history.undo(editor); // Back to "Hello World"
history.undo(editor); // Back to "Hello "
history.undo(editor); // Back to ""
}
}
Bad vs Good
Bad Approach
Problems
- Undo logic requires exposing all internal fields via public getters and setters.
- The Caretaker code must know every single field to save — violently breaking encapsulation.
- State corruption risk — partial saves leave the object in a broken state.
class UndoManager {
private String savedContent;
private int savedCursor;
public void save(TextEditor editor) {
savedContent = editor.getContent(); // Exposes internal state
savedCursor = editor.getCursorPosition();
}
public void undo(TextEditor editor) {
editor.setContent(savedContent); // Exposes destructive setters
editor.setCursorPosition(savedCursor);
}
}
Better Approach
Improvements
- Memento is an opaque object — the caretaker safely stores it but cannot peek inside.
- The Originator retains total control over what gets saved and restored.
- Encapsulation remains completely intact.
History history = new History();
history.save(editor); // Opaque snapshot stored safely
editor.write("text");
history.undo(editor); // Originator restores itself securely
Pros vs Cons
| Pros | Cons |
|---|---|
| Safely saves and restores state without breaking encapsulation | Highly memory-heavy if state snapshots are large or frequent |
| Enables powerful multi-level undo/redo via simple stacks | Caretakers must manually manage memento lifecycles |
| Simplifies the Originator's code by moving history externally | Deep-copying is strictly required for mutable state in mementos |
| Easy to add redo functionality |
When to Use
- You need to produce snapshots of an object's state to restore a previous state (e.g., Undo/Redo, Save/Load).
- Direct access to the object's fields/getters/setters violates its encapsulation.
- You are implementing database-like transaction rollbacks in memory.
When Not to Use
- The object's state is trivially small and completely public anyway.
- Generating snapshots consumes massive amounts of RAM, making history tracking unfeasible.
Real-world Examples
java.io.Serializable(Serialization captures object state and allows restoration).javax.faces.component.StateHolder(JSF state saving mechanism).- GUI Frameworks implementing robust Undo/Redo stacks.
Key Takeaway
The Memento Pattern creates impenetrable snapshots of an object's state, enabling undo/redo functionality without bleeding private data into public APIs. Use it when you need point-in-time recovery, but implement strict retention policies (e.g., "max 50 undos") to ensure your snapshots don't consume all available memory.