Memento
Occasional. Turns up in specific problems. Worth recognising and being able to sketch, not worth drilling.
Start with the problem
Take corpus/shopping-cart's domain: a cart holding line items and applied coupons. A shopper adds a coupon, the total drops, and then clicks undo. The obvious first move is an undo() method on Cart itself, which stashes a copy of its own fields before every mutating call and restores them on request.
Watch where it goes
That works for one step back. Then the product wants a "recently undone" list showing several steps, rather than the last one alone. Cart's internal history now has to become a list instead of a single saved copy. Every field Cart ever grows needs a matching field in whatever struct it carries around for its own undo history. Cart's job was pricing and line items. It has now also become the class that owns a change log, and every new field has to be kept in two places by the same author, by hand.
The move
Split the two jobs. Cart produces an opaque snapshot of itself on request, and something outside Cart decides how many snapshots to keep and when to hand one back.
final class Cart {
private List<LineItem> lines;
private List<Coupon> coupons;
Snapshot save() {
return new Snapshot(List.copyOf(lines), List.copyOf(coupons));
}
void restore(Snapshot snapshot) {
this.lines = new ArrayList<>(snapshot.lines());
this.coupons = new ArrayList<>(snapshot.coupons());
}
record Snapshot(List<LineItem> lines, List<Coupon> coupons) {}
}
CartHistory, the class that wants several steps of undo, holds a Deque<Cart.Snapshot> and calls cart.save() before a change and cart.restore(snapshot) to go back. It never reads a field out of Snapshot directly, so Cart stays free to change its own internal representation without breaking whatever is stacking these tokens.
This is Memento. An originator produces an opaque token holding its own state, and a caretaker outside it decides how many tokens to keep, without ever looking inside one.
What modern Java changes here
A record is close to a free Memento. GoF's version needs a separate ceremony to keep the token opaque to everyone but the originator, usually a private inner class with restricted visibility, enforced by convention rather than the compiler. A record gives the same immutability and a working equals for free. Keeping the record package-private or nested inside the originator gets the same "only the originator constructs and reads it" property, using ordinary Java access rules and nothing extra.
When naming it is wrong
If only one step of undo is ever needed, a plain "previous state" field on the object itself, restored on demand, is enough. That is not a smaller Memento. It is the case where the split has not earned a second class yet. Reach for the caretaker split once more than one snapshot needs to be kept. The other trigger is something outside the class that owns the state needing to decide how many steps back to keep, and for how long.
Where this lives in the app
No undo feature and no save-and-restore of a snapshot exists anywhere in this app's corpus. The anchor given for this page is B6, corpus/trip-state-machine. Reading it confirms the mismatch. B6 teaches a transition table for a trip's forward-only state machine, requested to matched to completed. Its worked material is entirely about which state comes next, never about reverting to a state already left.
The Cart and CartHistory classes above were built for this page, in the domain of corpus/shopping-cart, and are not code that exists in that corpus problem.