Courses › Low-Level Design › Foundations
Design Patterns
The handful of patterns that actually show up: Strategy, Factory, Observer, State and more.
There are 23 classic Gang of Four patterns, but only a handful come up in low-level design interviews. Know these well — when to use them and how to implement them in a few lines — and you'll be well equipped.
Strategy
Use when an algorithm or rule varies and should be swappable: pricing, spot allocation, elevator scheduling, rate-limiting algorithms.
from abc import ABC, abstractmethod
class AllocationStrategy(ABC):
@abstractmethod
def find_spot(self, levels, vehicle): ...
class LowestLevelFirst(AllocationStrategy):
def find_spot(self, levels, vehicle):
for level in levels:
spot = level.first_free_spot(vehicle.size)
if spot:
return spot
return NoneThis is the single most useful pattern in LLD interviews.
State
Use when an object's behavior depends on its state and transitions are well defined: a vending machine (idle → has money → dispensing), an order (created → paid → shipped), an elevator, a traffic light.
Each state is a class implementing the same interface; the context delegates to its current state, and states trigger transitions. This replaces sprawling if state == ... checks in every method.
class VendingState(ABC):
def insert_money(self, machine, amount): raise InvalidAction()
def select(self, machine, slot): raise InvalidAction()
class Idle(VendingState):
def insert_money(self, machine, amount):
machine.balance += amount
machine.state = HasMoney()
class HasMoney(VendingState):
def insert_money(self, machine, amount):
machine.balance += amount
def select(self, machine, slot):
machine.dispense(slot)
machine.state = Idle()Observer
Use when several components need to react to an event without the source knowing about them: display boards updated when spots change, notifications when an order ships.
class EventBus:
def __init__(self): self.subscribers = {}
def subscribe(self, event, handler):
self.subscribers.setdefault(event, []).append(handler)
def publish(self, event, payload):
for handler in self.subscribers.get(event, []):
handler(payload)Factory
Use when object creation involves logic or choosing between types: creating the right Vehicle from input, building a rate limiter from configuration. Keeps construction logic in one place.
Singleton
Use when exactly one instance must exist — but use it sparingly. It's essentially global state and makes testing harder. In interviews, prefer creating one instance and injecting it; mention singleton only if the problem explicitly calls for it.
Decorator
Use when adding responsibilities to an object dynamically: adding toppings to a coffee order, adding logging or caching around a service. Each decorator wraps the same interface.
Builder
Use when constructing objects with many optional parameters, to avoid huge constructors.
Command
Use when you need to queue, log or undo operations: a text editor's undo stack, elevator requests as objects, job queues.
Choosing patterns in an interview
[!WARNING] Patterns are a means, not a goal Using six patterns in a 45-minute interview usually signals over-engineering. Use a pattern when a requirement calls for it, and say which requirement.