Explainstuff.mebeta
All concepts
Basicsintermediate9 min

Design Patterns

Named, reusable solutions to the design problems that keep showing up — and a shared vocabulary for talking about them.

Once you have written enough software, you start noticing that the same design problems keep coming back. Different projects, different languages, but the same shape of puzzle: how do I create objects without hard-coding their concrete types, how do I let two incompatible interfaces talk to each other, how do I let one part of a system react to changes in another. Design patterns are the well-worn, named solutions to exactly these recurring problems.

What design patterns actually are

A design pattern is a description of a proven solution to a common design problem, written at the level of how the pieces relate rather than as a finished chunk of code. Think of it as a template you adapt to your situation, not a function you paste in. The biggest payoff is shared vocabulary: when a teammate says "let's use an Adapter here" or "that's just an Observer," everyone instantly pictures the same structure, the same trade-offs, and the same intent — without anyone drawing it on a whiteboard.

It is just as important to know what patterns are not. They are not copy-paste code, and they are not a goal in themselves. A codebase is not better because it uses a Factory; it is better because the Factory solved a real problem cleanly. Patterns are a means to an end — clearer, more flexible designs — and reaching for one when there is no problem to solve usually makes things worse, not better.

Note

Most of the classic patterns were catalogued in the 1994 book Design Patterns by Gamma, Helm, Johnson, and Vlissides — collectively nicknamed the "Gang of Four" (GoF). They sorted the patterns into three families: Creational, Structural, and Behavioral. Those names are still the common language teams use today.

The three families

The Gang of Four grouped patterns by the kind of problem they address. Creational patterns are about how objects get made — for example, a Factory decides which concrete class to instantiate, a Builder assembles a complex object step by step, and a Singleton ensures there is exactly one shared instance. These let you create objects without scattering new SomeClass() and its assumptions throughout your code.

Structural patterns are about how objects are composed into larger structures. An Adapter wraps an existing class so it fits an interface a client expects, a Decorator layers extra behavior onto an object without changing its class, and a Facade hides a messy subsystem behind one simple front door. They help you fit pieces together without rewriting the pieces.

Behavioral patterns are about how objects communicate and divide responsibility. An Observer lets many objects react when one object changes, a Strategy lets you swap an algorithm at runtime, and a Command wraps a request as an object you can queue, log, or undo. We will walk through the Observer pattern in detail next, because it is one of the clearest examples of why patterns earn their keep.

A worked example: the Observer pattern

Imagine a piece of state that several other parts of your system care about — say a stock price, a document being edited, or a sensor reading. The naive approach is to have the price object reach out and directly call the chart, the alert service, and the logger every time it changes. That hard-wires the price to everything that depends on it, and you have to edit the price object every time a new dependent appears.

The Observer pattern flips this around. The thing being watched — the Subject — keeps a list of Observers that have registered their interest. When the Subject's state changes, it simply walks that list and notifies each Observer; it neither knows nor cares what they do with the news. The chart redraws, the alert service checks a threshold, the logger writes a line — each reacts in its own way. The animation below shows that one-to-many fan-out: a single change in the Subject rippling out to every registered Observer at once.

The Observer pattern
notify
Subject
Observer
Observer
Observer
When the subject's state changes, it notifies every registered observer — one to many.

The win is decoupling. The Subject depends only on a small "observer" interface, not on the concrete chart or logger, so you can add or remove observers without ever touching the Subject. This is exactly the idea behind pub/sub messaging, just expressed at the level of objects in a single process: a publisher broadcasts, and any number of subscribers listen, with neither side tightly bound to the other.

How patterns support good design

Patterns are not arbitrary tricks — most of them are concrete ways to honor the SOLID principles. Observer and Strategy lean on programming to an interface, which is the open/closed and dependency-inversion ideas in action: you extend behavior by adding new classes rather than editing existing ones. Adapter and Facade keep responsibilities separated so each class has a single reason to change.

The common thread is low coupling. Nearly every pattern works by inserting a small, stable interface between two things that would otherwise be welded together, so each side can change independently. When you find yourself reaching for a pattern, it is usually because you sensed two parts of the system were too tightly bound — and the pattern gives you a tested way to loosen that knot.

The danger of overuse

The most common mistake with design patterns is using too many of them, too early. Once you learn the catalog, every problem starts to look like it needs a Factory wrapped in a Strategy behind a Facade. This "pattern-itis" buries simple logic under layers of indirection, making the code harder to read and slower to change — the exact opposite of what patterns are supposed to deliver.

The rule of thumb is to reach for a pattern when the problem it solves actually shows up, not preemptively in case it might. A plain function or a simple if statement is often the right answer. Let the pain be real first: when you genuinely feel a class is hard to extend or two modules are too entangled, then introduce the pattern that addresses that specific pain. A pattern applied to a problem you do not have is just extra complexity with a fancy name.

Watch out

Don't pattern for the sake of patterns. Adding a pattern before you have the problem it solves trades a little duplication for a lot of indirection. If you cannot name the concrete pain a pattern relieves, you probably don't need it yet — start simple and refactor toward a pattern when the need is real.

Key takeaways

  • Design patterns are named, reusable solutions to recurring design problems — and a shared vocabulary, not copy-paste code or a goal in themselves.
  • The Gang of Four sorted them into three families: Creational (Factory, Builder, Singleton), Structural (Adapter, Decorator, Facade), and Behavioral (Observer, Strategy, Command).
  • The Observer pattern has a Subject keep a list of Observers and notify them all on change — a one-to-many fan-out that decouples publisher from subscribers, just like pub/sub.
  • Most patterns work by inserting a small, stable interface between two parts, which supports SOLID and keeps coupling low.
  • Beware pattern-itis: reach for a pattern only when its problem actually appears, never preemptively.

Keep going