bigonotes
CS ConceptsDesign Patterns

How to Read a Design Pattern

The standard sections of a pattern description, which ones to read first, and a worked Observer example.

4 min readLast updated Design Patterns

Recipes all share a format: ingredients, prep time, steps, serving suggestions. Design patterns are documented the same way. Once you know the template, you can pick up any pattern from a book or website quickly and know where to look for what you need.

The anatomy of a pattern description#

At its core, every pattern answers three questions:

Part Question it answers
Problem What pain does it solve?
Solution What's the blueprint?
Consequences What are the trade-offs?

Read the problem first. Without it, patterns feel like solutions looking for problems.

The Gang of Four book expands this into a fuller template:

Section What it tells you
Name The handle you use in conversation ("let's use a Singleton")
Classification Creational, Structural or Behavioral
Intent One or two sentences: what the pattern does (its elevator pitch)
Also known as Other names for the same idea
Motivation A concrete scenario showing the problem
Applicability Situations where the pattern fits
Structure A class diagram of the participants
Participants / Collaborations Each class's role and how they work together
Consequences Benefits, costs and trade-offs
Implementation / Sample code Tips, pitfalls and code
Known uses Real systems that use it
Related patterns Similar or complementary patterns

Worked example: Observer#

Here is the template filled in for one pattern.

Classification: Behavioral.

Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Also known as: Publish-Subscribe, Dependents, event listener.

Motivation. A weather station collects temperature and humidity. Several displays need the data: current conditions, statistics, forecast, a mobile app. The naive design has the station call each one:

weather_station.update_current_display()
weather_station.update_stats_display()
weather_station.update_forecast()
weather_station.update_mobile_app()

The station now knows every display (tight coupling), adding a display means editing the station, and you can't add or remove displays at runtime.

Structure. The station holds a list of observers and calls update() on each one; every display implements the same interface.

Observer structure: WeatherStation holds a list of Observers and notifies them; CurrentDisplay, StatsDisplay and MobileApp implement the Observer interface

Applicability. Use Observer when:

  • A change to one object requires changing others.
  • You don't know in advance how many objects need to react.
  • An object should notify others without knowing who they are.

Real-world examples: newsletter subscriptions, YouTube subscribers, stock price alerts, GUI event handling.

Consequences.

Pros Cons
Loose coupling between subject and observers Notification order is not something you should rely on
Observers can be added or removed at runtime Forgotten observers cause memory leaks
Subject doesn't know concrete observer classes Cascading updates can be hard to debug
Supports broadcast communication Many observers means slower notifications

Implementation.

from abc import ABC, abstractmethod


class Observer(ABC):
    @abstractmethod
    def update(self, temp: float, humidity: float) -> None: ...


class Subject(ABC):
    @abstractmethod
    def attach(self, o: Observer) -> None: ...

    @abstractmethod
    def detach(self, o: Observer) -> None: ...

    @abstractmethod
    def notify_observers(self) -> None: ...


class WeatherStation(Subject):
    def __init__(self) -> None:
        self._observers: list[Observer] = []
        self._temperature = 0.0
        self._humidity = 0.0

    def attach(self, o: Observer) -> None:
        self._observers.append(o)

    def detach(self, o: Observer) -> None:
        self._observers.remove(o)

    def set_measurements(self, t: float, h: float) -> None:
        self._temperature = t
        self._humidity = h
        self.notify_observers()  # every state change triggers a broadcast

    def notify_observers(self) -> None:
        for o in self._observers:
            o.update(self._temperature, self._humidity)

Known uses. GUI toolkits' event callbacks (Tkinter's bind), Python's logging handlers, JavaScript EventEmitter and DOM events, .NET events and delegates, the model-notifies-views part of MVC.

Related patterns. Mediator centralizes complex communication; an event bus is a centralized variant of Observer.

Which sections to read, and when#

Which sections matter most depends on why you're reading.

You are... Read first
Learning a pattern Intent → Motivation → Structure → Implementation
Evaluating whether it fits Applicability → Consequences → Known uses
Implementing it Implementation → Consequences (what to watch out for) → Related patterns

What to learn first#

You don't need all 23 patterns at once. Learn them in roughly this order:

Tier Patterns
Essential Singleton, Factory Method, Observer, Strategy, Decorator
Very useful Adapter, Builder, Command, Facade, Iterator
Good to know Template Method, State, Proxy, Composite, Abstract Factory

Key takeaways#

  • Every pattern description answers: what problem, what solution, what trade-offs.
  • The intent is the one-line summary; the motivation is the problem in context.
  • Read applicability and consequences before deciding to use a pattern.
  • Start with the essential five; most real code uses a handful of patterns over and over.

Found a mistake? Tell me on LinkedIn ↗

Further reading#

  • Pattern Reference Cards: this template filled in for seven common patterns
  • The Gang of Four
  • Gamma, Helm, Johnson, Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (1994)