The classic Gang of Four patterns, grouped into the three families — creational, structural, behavioral — each with its intent, when to reach for it, and a diagram of how the pieces fit.
A design pattern is a named, reusable solution to a recurring design problem. Like code smells, the real value is shared vocabulary: once a team knows “Strategy” or “Adapter,” a whole structural idea travels in one word. The catalog below sticks to the patterns you actually meet: five creational (how objects get made), six structural (how objects are composed), and five behavioral (how objects collaborate and share responsibility).
Creational patterns are about how objects are made — hiding the concrete classes behind creation so client code depends on interfaces, not constructors.
Intent: ensure a class has exactly one instance and give a single global access point to it.
Use when: exactly one object must coordinate a resource — a config registry, a connection pool, a logger.
Watch out: it is global mutable state — it hides dependencies and hurts testability. Prefer passing the instance in (dependency injection) when you can.
getInstance() and receives the same object.Intent: put object creation behind one method that chooses and returns the right concrete product from a parameter, so callers never use new on concretes directly.
Use when: creation is simple and you want it in a single place. (Strictly an idiom, not a GoF pattern — but the natural stepping stone to the next two.)
if type == … choice; clients depend only on Product.Intent: define an interface for creating an object, but let subclasses decide which class to instantiate — the creation decision is deferred to derived classes via an overridable method.
Use when: a class cannot anticipate the concrete type it must create, or wants its subclasses to specify it.
factoryMethod(); each subclass returns its own product type.Intent: provide an interface for creating whole families of related objects without naming their concrete classes.
Use when: you must choose among consistent families (e.g. a UI toolkit’s Windows vs macOS widgets) and keep every product in a family compatible.
Intent: separate the construction of a complex object from its representation, assembling it step by step.
Use when: an object has many optional parts or a multi-step assembly — it replaces telescoping constructors and half-built objects.
Structural patterns are about how objects are composed into larger structures while keeping them flexible — mostly through wrapping and delegation rather than inheritance.
Intent: convert the interface of an existing class into the one a client expects, letting otherwise-incompatible classes work together — usually through composition (the adapter wraps the adaptee).
Use when: you must reuse a class whose interface does not match what your code needs.
Target to the client and translates each call to the adaptee’s existing API.Intent: provide one simplified, high-level interface to a complex subsystem — in effect, an adapter for a whole system.
Use when: you want to hide subsystem complexity and give clients a single, easy entry point.
Intent: attach additional responsibilities or attributes to an object dynamically by wrapping it — a flexible alternative to subclassing.
Use when: you need to add behavior to individual objects (not the whole class) and want to stack those additions freely.
Logging(Compressing(stream)).Intent: decouple an abstraction from its implementation so the two can vary independently.
Use when: both an abstraction and its implementation have several variants and you want to avoid a combinatorial explosion of subclasses (e.g. Shape × Renderer).
Intent: compose objects into tree structures and let clients treat individual objects (leaves) and groups (composites) through the same interface.
Use when: you have part-whole hierarchies — files and folders, UI element trees, nested menus.
Intent: provide a surrogate or placeholder for another object to control access to it.
Use when: you need lazy loading (virtual proxy), access control (protection proxy), caching, or a local stand-in for a remote object.
Behavioral patterns are about how objects collaborate — how responsibility and algorithms are assigned and how messages flow between them.
Intent: define a family of interchangeable algorithms, encapsulate each behind a common interface, and make them swappable at runtime — abstracting the algorithm from the code that uses it.
Use when: you have several variants of a behavior and want to pick one without a sprawl of conditionals.
strategy.execute(); which algorithm runs is just which object was plugged in.Intent: define a one-to-many dependency so that when one object changes state, all its dependents are notified automatically — publish/subscribe, a list of delegates/callbacks.
Use when: many objects must react to another’s change without the subject knowing their concrete types.
update() on each.Intent: define the skeleton of an algorithm in one method, deferring specific steps to subclasses.
Use when: several algorithms share the same overall structure but differ in a few steps — the skeleton stays fixed, the steps vary.
Intent: encapsulate a request as an object, so you can parameterize, queue, log, and undo operations.
Use when: you need undo/redo, task queues, or to decouple the object that invokes an operation from the one that performs it.
command.execute(); the command knows which receiver to act on and how to reverse it.Intent: let an object alter its behavior when its internal state changes — it appears to change class.
Use when: behavior depends heavily on state and you would otherwise write large conditionals branching on a state field. (Structurally like Strategy, but the states drive the transitions between themselves.)
Several patterns look alike on a class diagram; what separates them is intent. A few pairs worth keeping straight:
| If you are… | Reach for | Not |
|---|---|---|
| Choosing an algorithm at runtime | Strategy | State (which also transitions itself) |
| Changing behavior as internal state changes | State | Strategy (client picks; it doesn’t transition) |
| Making a mismatched interface fit | Adapter | Facade (simplifies, not translates) |
| Simplifying a whole subsystem | Facade | Adapter (one class, not a system) |
| Adding behavior by wrapping, same interface | Decorator | Proxy (controls access, doesn’t add features) |
| Controlling access / lazy-loading, same interface | Proxy | Decorator (enhances behavior) |
| Deferring which class to instantiate to a subclass | Factory Method | Abstract Factory (whole families) |
| Creating consistent families of products | Abstract Factory | Factory Method (single product) |
| Decoupling two dimensions that each vary | Bridge | Adapter (fixes an existing mismatch) |