Lesson 3 / 25
When Patterns Help and When They Hurt
Patterns solve problems; they are not goals.
Pull patterns in, do not push them
Patterns help when you actually have the problem they solve: several interchangeable algorithms (Strategy), an incompatible third-party API (Adapter), a complex object with many optional parts (Builder). They hurt when added speculatively: an interface with one implementation, a factory that only ever creates one class, or five layers of indirection for a CRUD screen. This is premature abstraction; it makes code harder to read and change. A good habit is to write the simple version first, and refactor towards a pattern when duplication or change requests show you where the flexibility is needed. Also note that many GoF patterns compensate for missing language features; in languages with first-class functions, closures and modules, some patterns shrink to a few lines.
Over-engineered versus enough
The same tax calculation.
// Over-engineered: one rule, three abstractions
interface TaxStrategy { calculate(amount: number): number; }
class StandardTax implements TaxStrategy {
calculate(amount: number) { return amount * 0.2; }
}
class TaxStrategyFactory {
create(): TaxStrategy { return new StandardTax(); }
}
const tax = new TaxStrategyFactory().create().calculate(100);
// Enough, until a second tax rule actually appears
const TAX_RATE = 0.2;
const calculateTax = (amount: number) => amount * TAX_RATE;The rule of three
A common heuristic: tolerate duplication the first two times, and extract an abstraction when the third similar case appears and you can see what really varies.
Quick check: Which situation is most likely premature abstraction?
- A Builder for an object with many optional settings
- An Adapter wrapping an incompatible payment SDK
- A Strategy for three real shipping-cost rules
- An interface and factory for a class that has only one implementation and no planned variants
Answer
An interface and factory for a class that has only one implementation and no planned variants — Abstractions should follow real variation.