Design Patterns — A Practical Reference

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).

Patterns are tools, not goals. Reach for one because it solves a real problem in front of you — applying a pattern for its own sake just adds indirection. The catalog and names come from Design Patterns by Gamma, Helm, Johnson & Vlissides (the “Gang of Four”); this is an original practical summary.

Contents

  1. 1.1 Singleton
  2. 1.2 Simple Factory
  3. 1.3 Factory Method
  4. 1.4 Abstract Factory
  5. 1.5 Builder
  6. 2.1 Adapter
  7. 2.2 Facade
  8. 2.3 Decorator
  9. 2.4 Bridge
  10. 2.5 Composite
  11. 2.6 Proxy
  12. 3.1 Strategy
  13. 3.2 Observer
  14. 3.3 Template Method
  15. 3.4 Command
  16. 3.5 State
  17. Choosing between them

1. Creational Patterns

Creational patterns are about how objects are made — hiding the concrete classes behind creation so client code depends on interfaces, not constructors.

1.1 Singleton

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.

Client A Client B Client C Singleton − instance : static + getInstance() getInstance() → the one shared instance
Every caller goes through getInstance() and receives the same object.

1.2 Simple Factory

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.)

Client SimpleFactorycreate(type) «interface»Product ProductA ProductB creates
The factory owns the if type == … choice; clients depend only on Product.

1.3 Factory Method

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.

CreatorfactoryMethod() ConcreteCreatoroverrides factoryMethod() «interface»Product ConcreteProduct extends creates
The base algorithm calls factoryMethod(); each subclass returns its own product type.

1.4 Abstract Factory

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.

«interface» GUIFactorycreateButton() · createCheckbox() WinFactory MacFactory builds WinButton + WinCheckbox builds MacButton + MacCheckbox
Swap the factory and an entire, internally-consistent product family swaps with it.

1.5 Builder

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.

Director «interface» BuilderbuildPart() · getResult() ConcreteBuilder Product drives builds
The director says what steps run; the concrete builder decides how and yields the finished product.

2. Structural Patterns

Structural patterns are about how objects are composed into larger structures while keeping them flexible — mostly through wrapping and delegation rather than inheritance.

2.1 Adapter

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.

Client «interface»Target Adapter Adaptee(existing API) uses wraps
The adapter speaks Target to the client and translates each call to the adaptee’s existing API.

2.2 Facade

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.

Client Facade Subsystem A Subsystem B Subsystem C
One friendly method fans out to the many subsystem calls the client would otherwise juggle.

2.3 Decorator

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.

«interface»Component ConcreteComponent Decoratorwraps a Component has-a
A decorator is a Component and has a Component, so wrappers nest: Logging(Compressing(stream)).

2.4 Bridge

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).

Abstraction RefinedAbstraction «interface»Implementor ConcreteImplA ConcreteImplB has-a (bridge)
The abstraction delegates the work across the “bridge” to an implementor; each side grows on its own.

2.5 Composite

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.

«interface»Component Leaf Compositechildren : Component[] 0..* children
A composite holds children that are themselves Components, so the tree is uniform all the way down.

2.6 Proxy

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.

Client «interface»Subject Proxy RealSubject uses controls →
Same interface as the real object; the proxy decides whether, when, and how to forward each call.

3. Behavioral Patterns

Behavioral patterns are about how objects collaborate — how responsibility and algorithms are assigned and how messages flow between them.

3.1 Strategy

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.

Context «interface»Strategy StrategyA StrategyB has-a strategy
The context calls strategy.execute(); which algorithm runs is just which object was plugged in.

3.2 Observer

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.

Subjectobservers[] · notify() «interface»Observer ObserverA ObserverB notifies 0..*
Observers subscribe to the subject; on a change the subject walks its list and calls update() on each.

3.3 Template Method

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.

AbstractClass templateMethod() → step1(); step2() step1() · step2() (abstract) ConcreteClassA ConcreteClassB override step1() / step2()
The base class owns the ordering; subclasses fill in only the steps that differ (inverting control — “don’t call us, we’ll call you”).

3.4 Command

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.

Invoker «interface»Command ConcreteCommand Receiver holds calls
The invoker just calls command.execute(); the command knows which receiver to act on and how to reverse it.

3.5 State

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.)

Context «interface»State StateA StateB current state states transition to one another
The context forwards calls to its current state object, which may swap the context to a different state.

Choosing between them

Several patterns look alike on a class diagram; what separates them is intent. A few pairs worth keeping straight:

If you are…Reach forNot
Choosing an algorithm at runtimeStrategyState (which also transitions itself)
Changing behavior as internal state changesStateStrategy (client picks; it doesn’t transition)
Making a mismatched interface fitAdapterFacade (simplifies, not translates)
Simplifying a whole subsystemFacadeAdapter (one class, not a system)
Adding behavior by wrapping, same interfaceDecoratorProxy (controls access, doesn’t add features)
Controlling access / lazy-loading, same interfaceProxyDecorator (enhances behavior)
Deferring which class to instantiate to a subclassFactory MethodAbstract Factory (whole families)
Creating consistent families of productsAbstract FactoryFactory Method (single product)
Decoupling two dimensions that each varyBridgeAdapter (fixes an existing mismatch)
The recurring theme: creational patterns hide how objects are made, structural patterns compose objects through wrapping and delegation, and behavioral patterns assign responsibility and let objects talk. Learn the intent first — the class diagram is just how that intent is wired.