Originally published on phpdesignpatterns.dev — part of the PHP design patterns course. View this lesson on the original site.

Strategy: replacing branches with swappable behaviour

Welcome to PHP Design Patterns. Chapter by chapter we'll work through every pattern in the course — not just the Gang of Four — using PHP 8 idioms, real before/after refactors, and a hard line on when not to reach for a pattern. Patterns are tools, not trophies. The point of this course isn't to make you reach for one in every PR; it's to make you reach for the right one when the code stops being kind to its readers, and to leave the simpler shape alone the rest of the time.

Each chapter follows the same arc: a concrete piece of code that's hurting, the pattern that fixes it, the contract that makes the pattern work, the variants you'll see in real PHP codebases, the trade-offs and overkill cases, a guided refactor exercise, and a short quiz. The next four chapters (including this one) are free; the rest are paid. You'll get the most out of the course by working the refactors yourself in your editor before reading the solution.

Strategy pattern

Where we start: Strategy

Strategy is the natural first pattern because it solves the most common shape of code rot in business logic: a single function that does several genuinely different things depending on a flag, a type, or a string. You've seen it. A discount calculator that handled two customer types and now handles seven. A shipping cost function that started as "UK or rest of world" and now has rules for fourteen countries, four carriers, and a "next-day" override. The function still works. Tests pass. But reading it is exhausting and changing it is frightening.

The pressure that builds in code like this is variation. The same operation needs to behave differently depending on context, and the easiest way to express that — at first — is a chain of if/elseif or a switch. Strategy is the disciplined response when that chain stops being easy.

What Strategy actually is

Strategy lets you define a family of interchangeable behaviours, encapsulate each one in its own class behind a shared interface, and choose which one runs at the moment of use.

Broken down:

  • Family of behaviours. Several different ways to perform the same logical operation — calculate a discount, render a report, send a notification — that are siblings, not subtypes.
  • Shared interface. A single small contract — usually one method — that names the question and the answer. Every strategy implements it.
  • Encapsulated in its own class. Each behaviour lives in one file, with its own dependencies, its own tests, its own PR diff when it changes.
  • Chosen at the moment of use. The caller (or a small picker) decides which strategy to run for a given request. The strategies themselves don't know about each other.

The result: the long if chain disappears. The branching that was inside the function becomes a one-line lookup outside it. Each rule has a home. Adding a new rule is a new file plus one line in the picker, with nothing else changing.

The signal we're responding to

Three things tend to show up together when Strategy is the right move:

  1. A function that does the same job in several different ways depending on a flag, a type, or a string.
  2. A long block of conditionals where each branch has its own logic rather than a tiny tweak.
  3. A change request that says "can it also do X?" — and X is just another branch.

This chapter starts with that smell, looks honestly at what it costs the team, and then walks through how Strategy splits the variation out into named, testable, swappable objects.

What this chapter will not do

It won't tell you to reach for Strategy at the first if. Three branches of trivial logic do not need a pattern. The whole point of the chapter — and of this course — is to draw the line between "this is fine" and "this is hurting us" — and to apply Strategy only on the hurting side.

From PHP design patterns — Strategy: replacing branching with swappable behaviour