Capture and externalise an object's internal state so it can be restored later — without violating encapsulation.
The Memento pattern captures an object's complete internal state into a snapshot object (memento) that can be stored and later used to restore the original object to that exact state — without exposing the object's internals to the outside world.
Think of a video game save point: the game snapshots your character's level, health, position, and inventory. You can die and "load" back to that save. The save file (memento) doesn't expose how the game engine works internally — it just holds enough data to restore the state.
The pattern involves three objects: the Originator (the object being snapshotted), the Memento (the snapshot), and the Caretaker (manages the snapshots — knows when to save and restore, but never looks inside the memento).
Key Idea: Snapshot state into an opaque object. The Caretaker stores it; the Originator creates and consumes it. Encapsulation preserved throughout.
The object whose state needs to be saved. It creates mementos (save()) and restores from them (restore()). Only the Originator knows what needs to be captured and how to use it.
The snapshot object. Stores the Originator's state. Should be immutable (ideally) — state captured at save time should never change. The Caretaker holds it but cannot read its internals.
Manages when to save and restore. Holds a stack (or list) of mementos. It never inspects or modifies memento contents — it just stores and returns them when needed.
// Memento Pattern — Text Editor Undo History
// ─── Memento — stores the snapshot ───────────────────────────
class EditorMemento {
private final String content;
private final int cursorPosition;
private final String selectedText;
EditorMemento(String content, int cursor, String selected) {
this.content = content;
this.cursorPosition = cursor;
this.selectedText = selected;
}
// Only the Originator can access internals
String getContent() { return content; }
int getCursorPosition(){ return cursorPosition; }
String getSelectedText() { return selectedText; }
@Override
public String toString() {
return "Snapshot['" + content.substring(0, Math.min(20, content.length())) + "...']";
}
}
// ─── Originator — creates and restores mementos ───────────────
class TextEditor {
private String content = "";
private int cursor = 0;
private String selectedText = "";
public void type(String text) {
content = content.substring(0, cursor) + text + content.substring(cursor);
cursor += text.length();
System.out.println("[Editor] Content: "" + content + "" | Cursor: " + cursor);
}
public void select(int start, int end) {
selectedText = content.substring(start, end);
System.out.println("[Editor] Selected: "" + selectedText + """);
}
public EditorMemento save() {
EditorMemento m = new EditorMemento(content, cursor, selectedText);
System.out.println("[Editor] Saved " + m);
return m;
}
public void restore(EditorMemento memento) {
content = memento.getContent();
cursor = memento.getCursorPosition();
selectedText = memento.getSelectedText();
System.out.println("[Editor] Restored: "" + content + "" | Cursor: " + cursor);
}
}
// ─── Caretaker — manages history ──────────────────────────────
class History {
private java.util.Deque<EditorMemento> undoStack = new java.util.ArrayDeque<>();
private java.util.Deque<EditorMemento> redoStack = new java.util.ArrayDeque<>();
public void push(EditorMemento m) {
undoStack.push(m);
redoStack.clear();
}
public EditorMemento undo() {
if (undoStack.isEmpty()) { System.out.println("[History] Nothing to undo"); return null; }
EditorMemento m = undoStack.pop();
redoStack.push(m);
return undoStack.isEmpty() ? null : undoStack.peek();
}
public EditorMemento redo() {
if (redoStack.isEmpty()) { System.out.println("[History] Nothing to redo"); return null; }
EditorMemento m = redoStack.pop();
undoStack.push(m);
return m;
}
}
// ─── Client ───────────────────────────────────────────────────
public class App {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
History history = new History();
history.push(editor.save()); // save empty state
editor.type("Hello");
history.push(editor.save());
editor.type(", World!");
history.push(editor.save());
editor.type(" How are you?");
history.push(editor.save());
System.out.println("\n--- Undo ---");
EditorMemento prev = history.undo();
if (prev != null) editor.restore(prev);
System.out.println("\n--- Undo again ---");
prev = history.undo();
if (prev != null) editor.restore(prev);
System.out.println("\n--- Redo ---");
EditorMemento next = history.redo();
if (next != null) editor.restore(next);
}
}When users need to reverse actions — text editors, drawing apps, spreadsheets. Each action saves a memento; undo restores the previous one.
TextEditorHistory, CanvasUndoStackWhen an operation may fail and needs to be reversed — save state before the operation, restore if it fails.
DatabaseTransaction, BatchProcessorWhen players should be able to save progress and resume later — snapshot the entire game state to a saveable memento.
GameSaveSlot, CheckpointSystemWhen a multi-step process needs a "Back" button — save state at each step so the user can navigate back without losing progress.
CheckoutWizard, FormHistoryEvery git commit is a Memento — a complete snapshot of the repository state. git checkout and git revert restore from mementos. The .git directory is the Caretaker.
Java's Serializable interface lets objects serialise their state to bytes (creating a memento) and deserialise back. Used in session replication, deep cloning, and RMI.
history.pushState() and history.replaceState() save application state mementos. history.back() and history.forward() restore them — the browser manages the caretaker stack.
Redux DevTools stores every action's resulting state as a memento. Time-travel debugging restores the app to any past state by replaying the sequence of mementos.