Introduction to Design Patterns

A design pattern is a named solution to a recurring design problem. It is not a library. It is not "advanced OOP." It is a conversation shortcut: "the retry policy should be a Strategy" instead of a 20-minute whiteboard of if-else.

The "Gang of Four" (Gamma, Helm, Johnson, Vlissides) catalogued these in 1994. The problems still exist. The class diagrams in the book are optional; the intent is not.

[!TIP] ELI5: Cooking techniques "Sauté" is a pattern: hot pan, little oil, keep it moving. It is not a recipe for Tuesday. You still choose ingredients. Patterns are techniques. Using every technique in one omelette is how you get a mess.

Why bother naming them

If you cannot state the problem in one sentence, you do not need a pattern yet. You need a function.

The three families

Creational — how objects come to exist

The rest of the code should not new a concrete class in 40 places if the kind of object will change.

Pattern Problem
Factory Caller should not know which subclass to construct.
Singleton Exactly one instance (use sparingly; often a mistake).
Builder Many optional construction steps; telescoping constructors hurt.
Prototype Clone a configured object instead of reconstructing it.

Structural — how objects are composed

Pattern Problem
Adapter Foreign API does not match the one you designed for.
Decorator Add behavior (logging, retries) without exploding subclasses.
Facade Hide a gnarly subsystem behind one simple type.
Proxy Stand-in for access control, lazy load, or a remote call.

Behavioral — how objects divide work and talk

Pattern Problem
Strategy Swap an algorithm (sort, pricing, auth) at runtime.
Observer Many listeners should react to one subject's events.
Command Queue/undo an action as an object.
State Behavior changes when an object's state changes (order: draft → paid).

This track deep-dives the four you will actually be asked and will actually use in web backends: Factory, Singleton, Strategy, Observer.

A smell, then a pattern

Before (not a pattern, just pain):

function ship(order: Order, carrier: string) {
  if (carrier === "ups") { /* 40 lines */ }
  else if (carrier === "fedex") { /* 40 lines */ }
  else if (carrier === "dhl") { /* 40 lines */ }
}

Every new carrier edits this function (Open/Closed violation). Tests for UPS break when you touch DHL.

After (Strategy): each carrier is a ShippingCarrier with quote / book. ship takes the interface. Adding "USPS" is a new file, not a new else if.

That is the whole game: spot the variation, give it a type, inject it.

Over-engineering (the actual risk)

[!WARNING] Patterns are compression for experienced readers. For a junior reading your code, AbstractFactory of one implementation is noise. Prefer the simplest thing that keeps tomorrow's change local.

Red flags:

Green lights:

Next: Factory, then Singleton (including why not to), then Strategy and Observer with code.