Design Patterns for LLD Interviews: Strategy, Observer, Factory

The design patterns that come up in low-level design interviews: strategy, observer, factory, builder, singleton, decorator, adapter and state, with examples.

Intermediate 3 min read · Updated

Design patterns are proven, named solutions to recurring design problems in object-oriented code, catalogued most famously by the "Gang of Four" book. In low-level design interviews, a handful come up again and again: strategy, observer, factory, builder, singleton, decorator, adapter and state. The skill being tested isn't reciting them but recognising which problem in the requirements a pattern solves, and not using one where it doesn't.

price(cart)Checkoutneeds a pricePricingStrategyinterfaceRegularPricingSalePricingMemberPricing
The Strategy pattern: checkout depends on one interface, so a new pricing rule is a new class instead of another branch in an if-else chain.

The patterns that matter most

PatternProblem it solvesLLD example
StrategySwap an algorithm or policy at runtime without conditionalsPricing rules in a parking lot; rate limiting algorithms; payment methods
ObserverNotify many dependents when something changes, without coupling to themDisplay boards updating when spots free up; order status notifications
Factory (method / simple factory)Create objects without the caller knowing the concrete classCreate a Vehicle from a type; create a notifier per channel
BuilderConstruct complex objects step by step with optional partsBuilding an HTTP request or a pizza order with many options
SingletonExactly one shared instanceA configuration registry or a single parking lot instance (use sparingly)
DecoratorAdd behaviour to an object by wrapping it, without subclass explosionAdd logging, caching or retries around a service; toppings on a coffee
AdapterMake an incompatible interface fit the one you expectWrap a third-party payment SDK behind your PaymentGateway interface
StateBehaviour changes with an object's state; avoid big switch statementsVending machine, elevator, order lifecycle
CommandRepresent actions as objects to queue, log or undo themUndo/redo in an editor; job queues
Chain of responsibilityPass a request along handlers until one handles itMiddleware pipelines; approval workflows; logging levels

Strategy

interface PricingStrategy { price(entry: Date, exit: Date, vehicle: Vehicle): Money }
class HourlyPricing implements PricingStrategy { ... }
class WeekendFlatPricing implements PricingStrategy { ... }

class ParkingLot {
  constructor(private pricing: PricingStrategy) {}
  checkout(ticket: Ticket) { return this.pricing.price(ticket.entry, new Date(), ticket.vehicle) }
}

New pricing schemes are new classes; ParkingLot doesn't change. This is the open-closed principle in practice. See SOLID principles.

Observer

interface SpotListener { onSpotChanged(floor: number, freeSpots: number): void }

class Floor {
  private listeners: SpotListener[] = []
  subscribe(listener: SpotListener) { this.listeners.push(listener) }
  release(spot: Spot) {
    spot.free()
    for (const l of this.listeners) l.onSpotChanged(this.number, this.freeCount())
  }
}
// DisplayBoard and MobileAppNotifier both implement SpotListener

In distributed systems the same idea appears as pub/sub: producers publish events without knowing who consumes them.

State

A vending machine behaves differently when idle, when it has money, and when dispensing. Rather than checking a status flag in every method, each state is a class implementing the same interface (insertCoin, selectItem, dispense), and the machine delegates to its current state, which decides the next state. Invalid actions are rejected naturally in states that don't allow them.

Decorator and adapter

  • A decorator implements the same interface as the object it wraps: new RetryingClient(new LoggingClient(new HttpClient())) adds behaviours in layers, each independently testable.
  • An adapter translates: your code depends on PaymentGateway.charge(amount, token); an adapter maps that to a vendor SDK's different method names and types, so switching vendors touches one class.

Using patterns well in an interview

  • Start from requirements: "pricing varies by time and vehicle type and will change" → strategy. "Several displays need updates" → observer.
  • Name the change it enables each time you introduce a pattern.
  • Prefer composition (strategy, decorator, state) over deep inheritance trees.
  • Be careful with singletons: they hide dependencies and complicate tests and concurrency. Prefer passing one instance explicitly.
  • Mention thread safety where shared state appears, such as two gates assigning the same spot: lock per spot or use atomic operations.

Many architecture ideas are these patterns at a larger scale: an API gateway is a facade, a sidecar proxy is a decorator, a webhook is an observer across systems, and a job queue is the command pattern.

Where it’s used

LLD interviews

Parking lots, vending machines, elevators and ride booking use strategy, state, observer and factory.

Frameworks and libraries

Middleware chains, event listeners, builders and adapters appear throughout common frameworks.

Service code

Decorators add retries, caching and metrics around clients without changing them.

In an interview

  • Tie each pattern to a requirement and the change it makes easy.
  • Know strategy, observer, factory, state, decorator and adapter well enough to code them.
  • Prefer composition and avoid gratuitous singletons.
  • Discuss thread safety for shared state.

Further reading