Skip to main content

Choosing Patterns as a Response to Design Pressure

What This Concept Is

A design pattern is a named solution to a recurring pressure in code. It is not an ornament. It is not proof that you read a book. It is a deliberate response to a specific force that is pulling your design out of shape.

Three ideas have to travel together:

  • the pressure, named in plain language ("this conditional explodes every time a new payment type is added")
  • the pattern that absorbs it (Strategy, Factory, Decorator, and so on)
  • the cost the pattern adds (indirection, new files, new vocabulary for teammates)

If you cannot say all three out loud, you are not choosing a pattern. You are decorating.

Why It Matters Here

Most pattern misuse in real teams is not wrong syntax. It is wrong reason:

  • introducing Strategy because "subclassing is bad," not because behavior varies
  • introducing a Factory because "new is bad," not because construction is complex
  • introducing Observer because "coupling is bad," not because a real event actually has multiple listeners
  • introducing an Abstract Factory for a single product family that will never grow

Every such move pays a real cost (more files, more names, more indirection) and repays nothing, because the pressure never existed. Later modules (S4 systems, team work) will treat pattern overuse as a code smell in its own right.

Concrete Example

Feature: a checkout service that charges customers. Initially one method:

class Checkout:
def charge(self, order, method):
if method == "card":
# 30 lines of Stripe code
...
elif method == "paypal":
# 25 lines of PayPal code
...
elif method == "invoice":
# 20 lines of invoicing code
...

Pressure: every new payment method edits charge, risks touching the others, and makes the method grow past any sane length. That is the "divergent change" smell plus a "long function."

Response: Strategy. Each payment type becomes a PaymentProcessor with a common charge(order) method. Checkout now selects one.

class Checkout:
def __init__(self, processor: PaymentProcessor):
self.processor = processor
def charge(self, order):
return self.processor.charge(order)

Now the pressure is named ("payment methods vary and multiply") and the pattern is paying for itself.

If instead the system never has more than one payment method and never will, adding Strategy is pure cost.

Common Confusion / Misconception

The main trap is pattern-first thinking: "what pattern should I use here?" before you have named the pressure.

Another trap is pattern romanticism: believing that a recognizable pattern in the code is automatically a mark of quality. A well-named function and a small class often beat a pattern. Head First Design Patterns is explicit: patterns are heavyweight tools; reach for simpler tools first.

A third trap is premature generalization: reading a pattern into requirements that do not yet exist. If there is exactly one implementation of an interface today and no credible second one this quarter, there is no Strategy pressure yet.

How To Use It

Before applying any pattern, complete this sentence out loud:

I am introducing <pattern> because <specific pressure> is pushing the current design out of shape, and I accept the cost of <indirection / new files / new vocabulary> in exchange.

If you cannot fill all three blanks, do not apply the pattern yet. Write the simpler version, wait, and let the code tell you when pressure is real.

Checklist for the judgment:

  1. What is the smell or force I am responding to? (divergent change, shotgun surgery, conditional explosion, primitive obsession, etc.)
  2. Is it a current pressure or a speculated future one?
  3. What is the simplest change that would relieve it? (often: extract function, move method, replace conditional with polymorphism)
  4. If a pattern is still warranted, which one is just enough?

Check Yourself

  1. Give an example where introducing Strategy is wrong.
  2. Why is "the book says use Factory" not a valid justification in a review?
  3. What refactor should you usually try before reaching for a pattern?

Mini Drill or Application

For each situation, name the pressure in one sentence, then name the pattern only if one is warranted:

  1. a report generator with an if format == 'pdf'... chain that grows every quarter
  2. a User class that different subsystems want different "views" of, but there is only one subsystem today
  3. a notification system where adding a new channel requires changing ten unrelated classes
  4. a constructor that takes eleven arguments, half of them defaulting to None

Read This Only If Stuck