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.
Five components in a UI form — a checkbox, a dropdown, two text fields, and a submit button — all need to react when each other changes. The checkbox enables the dropdown; the dropdown choice changes which text fields are required; the submit button enables only when all required fields are filled. If each component talks directly to the others, you end up with 5 × 4 = 20 direct dependencies. Add a sixth component and you need to update potentially five existing ones. The Mediator pattern replaces all those direct links with a single hub: components talk only to the mediator, and the mediator coordinates everyone.
The pattern is best understood as the opposite of tight coupling. Without it, N components need O(N²) connections. With it, N components each need exactly 1 connection to the mediator — O(N) total. The tradeoff is that the mediator itself becomes a concentrator of coordination logic, so it needs to be kept focused and testable.
You have a BookShelf that stores books in an ArrayList internally. You want callers to be able to iterate over books without knowing it’s an ArrayList. Next month you might change it to a linked list or a tree structure sorted by author. If callers work directly with your internal list, every change to your storage structure breaks them. The Iterator pattern gives callers a stable traversal interface that survives any change to the underlying data structure.
This is one of the patterns you already use every day through Java’s for-each loop. Every class that implements Iterable<T> is participating in this pattern. Understanding the mechanics helps you write cleaner custom collections and explains why certain traversal approaches are more maintainable than others.
Every text editor has Ctrl+Z. Every transaction system has rollback. Every task queue has deferred execution. All of them rely on the same underlying idea: wrap a request in an object so you can store it, pass it around, and undo it later. That’s the Command pattern. When an action becomes an object, everything you can do with objects — store in a collection, serialize to disk, execute at a scheduled time, replay in order, reverse — becomes available to your actions.
The pattern separates the code that decides to do something (the Invoker) from the code that knows how to do it (the Receiver) via a Command object that encapsulates the call. This separation is what makes undo possible: the command object knows both how to execute and how to reverse its own effect.
A support ticket comes in. Should it go to Level-1 support, Level-2 engineering, a senior architect, or the on-call incident team? The severity determines the right handler — but the code submitting the ticket shouldn’t need to know the routing rules. The Chain of Responsibility pattern lets you define a sequence of handlers, each deciding whether to handle a request or pass it to the next handler in the chain. The sender fires into the chain; the chain figures out who deals with it.
This pattern shows up in more places than you might expect: Java Servlet filters, Spring’s interceptor chain, Java’s logging framework, and every middleware pipeline in modern web frameworks all use the same structure. Understanding it from first principles makes those frameworks far less mysterious.
Your service holds a database connection that takes 200ms to open. On most requests you don’t need it — the data is already cached or the request is served from memory. With the Proxy pattern, you can give every caller a DatabaseConnection object at startup but only open the real connection the first time a query is actually executed. The caller’s code doesn’t change. The real connection object doesn’t change. The proxy sits in between and makes the decision.
Proxy is one of the most widely used patterns in enterprise Java. Every time you use Spring’s @Transactional, @Cacheable, or @Async, you’re working through a proxy. Hibernate’s lazy-loaded entities are proxies. Java’s java.lang.reflect.Proxy creates proxies dynamically at runtime. Understanding the pattern means understanding a large chunk of how modern frameworks work.
A game renders a forest of 100,000 trees. Each tree has a position, but also a species name, color values, and a texture bitmap that might be several megabytes. Storing that texture in every tree object would exhaust memory for any meaningful forest. The Flyweight pattern solves this by separating the data that’s unique to each object (position) from the data that’s shared across thousands of identical objects (species, texture) — and storing the shared part only once.
Flyweight is the most performance-oriented structural pattern in the GoF catalogue. It’s not about flexibility or interface design — it’s about fitting more objects into the same memory by eliminating duplication. When you know a large number of objects will share most of their state, Flyweight gives you a principled way to exploit that sharing.