Undo is a feature users take for granted but developers often implement badly. The naive approach — saving the entire object state as a public snapshot — violates encapsulation: once you expose internal state for storage, anything can read and modify it. The Memento pattern solves this by defining a dedicated snapshot object whose contents only the originator can read. The caretaker (your undo manager) stores and returns mementos without ever opening them.
This is not just about text editors. Any time you need rollback — database transactions, game save states, configuration checkpoints, wizard “back” buttons — you’re solving the same problem the Memento pattern addresses.
All code compiles and runs with Java 25. No external dependencies required.
Pattern Structure

- Originator — the object whose state you’re saving (
Editor). Creates mementos from its current state; restores state from a memento. - Memento — the snapshot (
EditorMemento). Immutable. Only the originator can read its contents. - Caretaker — manages the history (
History). Holds mementos but never inspects their contents.
The Memento: Immutable and Opaque
Package-private accessors are the Java idiom for keeping memento state opaque. The getContent() and similar methods are accessible from within the memento package — the editor can call them — but external code in other packages cannot. The caretaker stores EditorMemento references but can only call what’s public (just toString()).
EditorMemento.java
package memento;
/**
* Memento — a snapshot of the editor's state.
* Immutable: once created, the state cannot be changed.
* The Caretaker holds these; the Originator creates and restores from them.
*/
public final class EditorMemento {
private final String content;
private final int cursorPosition;
private final String selectedText;
EditorMemento(String content, int cursorPosition, String selectedText) {
this.content = content;
this.cursorPosition = cursorPosition;
this.selectedText = selectedText;
}
// Package-private: only the Editor (Originator) should read the state back
String getContent() { return content; }
int getCursorPosition() { return cursorPosition; }
String getSelectedText() { return selectedText; }
@Override
public String toString() {
return "Snapshot[content=\"" + content + "\", cursor=" + cursorPosition + "]";
}
}
The Originator (Editor)
The editor owns both save() and restore(). It packs all internal fields into a memento on save, and unpacks them from a memento on restore. No other class needs to know that the editor has a cursor position or a selection.
Editor.java
package memento;
/**
* Originator — the text editor whose state we're saving and restoring.
* Creates mementos and restores from them; no history management here.
*/
public class Editor {
private String content = "";
private int cursorPosition = 0;
private String selectedText = "";
public void type(String text) {
content = content.substring(0, cursorPosition) + text + content.substring(cursorPosition);
cursorPosition += text.length();
}
public void selectText(int start, int end) {
this.selectedText = content.substring(start, end);
System.out.println(" Selected: \"" + selectedText + "\"");
}
public void deleteSelection() {
content = content.replace(selectedText, "");
selectedText = "";
}
/** Create a snapshot of current state */
public EditorMemento save() {
return new EditorMemento(content, cursorPosition, selectedText);
}
/** Restore state from a snapshot */
public void restore(EditorMemento memento) {
this.content = memento.getContent();
this.cursorPosition = memento.getCursorPosition();
this.selectedText = memento.getSelectedText();
}
@Override
public String toString() {
return "Editor[\"" + content + "\", cursor=" + cursorPosition + "]";
}
}
The Caretaker (History)
The history stack holds mementos and hands them back on undo. It never calls any getter on the memento — it can’t, because the getters are package-private. It only stores and retrieves opaque objects.
History.java
package memento;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Caretaker — manages the stack of mementos.
* It does NOT open or inspect the mementos; it just stores and returns them.
*/
public class History {
private final Deque<EditorMemento> snapshots = new ArrayDeque<>();
public void save(EditorMemento memento) {
snapshots.push(memento);
System.out.println(" [History] Saved: " + memento);
}
public EditorMemento undo() {
if (snapshots.isEmpty()) return null;
return snapshots.pop();
}
public int size() { return snapshots.size(); }
}
Main wires the editor and history together, saving a snapshot after each edit — except the last one, which is intentionally left unsaved so it’s lost on undo:
Main.java
package memento;
/**
* Memento Design Pattern — Runnable Demo
* Run: javac memento/*.java -d out/memento && java -cp out/memento memento.Main
* Article: https://ankurm.com/memento-design-pattern-java/
*/
public class Main {
public static void main(String[] args) {
System.out.println("=== Memento Design Pattern Demo ===\n");
Editor editor = new Editor();
History history = new History();
System.out.println("Initial: " + editor);
editor.type("Hello");
history.save(editor.save());
System.out.println("After type: " + editor);
editor.type(", World");
history.save(editor.save());
System.out.println("After type: " + editor);
editor.type("! How are you?");
System.out.println("After type: " + editor);
System.out.println("\n-- Undo --");
editor.restore(history.undo());
System.out.println("After undo: " + editor);
editor.restore(history.undo());
System.out.println("After undo: " + editor);
System.out.println("\nHistory empty: " + (history.size() == 0));
System.out.println("\n=== Demo complete ===");
}
}
Console Output
Initial: Editor[“”, cursor=0]
[History] Saved: Snapshot[content=”Hello”, cursor=5]
After type: Editor[“Hello”, cursor=5]
[History] Saved: Snapshot[content=”Hello, World”, cursor=12]
After type: Editor[“Hello, World”, cursor=12]
After type: Editor[“Hello, World! How are you?”, cursor=26]
— Undo —
After undo: Editor[“Hello, World”, cursor=12]
After undo: Editor[“Hello”, cursor=5]
History empty: true
=== Demo complete ===
⚠️ Memory Cost: Each save() creates a full copy of the originator’s state. For a text editor with a 100KB document, saving a checkpoint every keystroke would consume memory fast. Real implementations use one of: bounded history (keep last N checkpoints only), delta snapshots (store only what changed), or lazy snapshots (save only on explicit user action, not automatically). Choose based on your undo granularity requirements and object size.
Serialization as Memento
Java’s Serializable mechanism is a Memento implementation. The JVM serializes an object’s state to a byte array (the memento), which can be stored anywhere — disk, network, database. Deserializing it later restores the object to the exact state it was in. This is how Java game saves, session persistence, and deep-clone via serialization work.
// Serialization as Memento
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new ObjectOutputStream(baos).writeObject(editor); // save()
byte[] snapshot = baos.toByteArray(); // the "memento"
// Later...
Editor restored = (Editor) new ObjectInputStream(
new ByteArrayInputStream(snapshot)).readObject(); // restore()
When to Use Memento
Reach for Memento when: you need undo/redo and the state being saved is genuinely encapsulated (clients shouldn’t see the internals). You need checkpoint/restore for a transactional operation that might need to rollback. You want to take snapshots for game save states, workflow wizard back-steps, or test fixture setup.
Avoid it when: the originator’s state is huge and snapshots are expensive — consider delta encoding or event sourcing instead. The state is already public and there’s no encapsulation to protect — a simple copy constructor or clone() is simpler than a full Memento implementation.
✅ Command + Memento Together: Command gives you fine-grained undo when each command knows its own inverse (insert → delete, add → remove). Memento gives you coarser undo when the state change is complex and hard to invert procedurally. Many real systems use both: Command for reversible operations and Memento for checkpoints before complex multi-step operations where a Command-based inverse would be too complicated to implement reliably.
Runnable Code on GitHub
Full source at ankurm.com/git.app/asmhatre/design-patterns under 03-behavioral/memento/.
javac 03-behavioral/memento/*.java -d out/memento
java -cp out/memento memento.Main
See Also
- Command Design Pattern in Java — encodes undo procedurally; Memento encodes undo as state snapshot; use Command when the inverse is computable, Memento when it’s not
- Prototype Design Pattern in Java — cloning is a form of state capture; Prototype for creating similar objects, Memento for rollback
Further Reading
- Memento — Refactoring.Guru
- Design Patterns: Elements of Reusable Object-Oriented Software — Gamma et al., Chapter 5
No Comments yet!