Hirestack
Hirestack
Chapter 3

Design patterns refresher

📖 3 min read · 4 sections · May 2026

Design patterns are reusable solutions to common design problems. The Gang of Four book (1994) catalogued 23; modern practice uses a subset frequently. Below are the ones you'll meet in LLD interviews.

Creational patterns — how objects come into being

Singleton
Ensures a class has exactly one instance. Useful for genuinely-singular resources (configuration, in-process registry, logger). Often abused: many "singletons" are really just globals in disguise. Prefer dependency injection for testability.

When to use: when there's a genuine single-shared resource and global access is acceptable.

When to avoid: anywhere you might want to swap implementations (testing, multi-tenancy, etc.).

Factory Method / Abstract Factory
Creates objects without specifying the concrete class. Caller asks the factory; factory decides which subclass to instantiate.

When to use: when the choice of concrete class depends on runtime state (e.g., VehicleFactory creating Car/Motorcycle/Truck based on input).

classDiagram
    direction LR
    class VehicleFactory {
      +create()
    }
    class Vehicle
    class Car
    class Motorcycle
    class Truck
    VehicleFactory --> Vehicle
    Car ..|> Vehicle
    Motorcycle ..|> Vehicle
    Truck ..|> Vehicle
  
Builder
Constructs complex objects step by step. Useful when an object has many parameters and immutability is desired.

Java example: Pizza.builder().withSize("Large").withTopping("Cheese").withTopping("Mushrooms").build().

Structural patterns — how objects compose

Composite
Treats individual objects and compositions uniformly. The canonical example: a file system. File and Directory both implement FileSystemNode; Directory contains a list of nodes (which may be Files or other Directories).
Decorator
Adds responsibilities to an object dynamically by wrapping it. Coffee with milk = MilkDecorator(Coffee()); Coffee with milk and sugar = SugarDecorator(MilkDecorator(Coffee())).
Adapter
Converts an interface to one a client expects. Useful for integrating third-party code with incompatible interfaces.
Facade
Provides a unified, simpler interface to a complex subsystem. The "front door" to multiple internal services.

Behavioral patterns — how objects communicate

Strategy
Encapsulates an algorithm in a class; client picks which strategy to use at runtime.

Canonical use: PaymentMethod interface with implementations CardPayment, UpiPayment, etc. Caller passes the desired strategy; behavior varies per call.

classDiagram
    direction LR
    class Context {
      +setStrategy()
      +execute()
    }
    class Strategy {
      +execute()
    }
    class CardPayment
    class UpiPayment
    class WalletPayment
    Context o-- Strategy
    CardPayment ..|> Strategy
    UpiPayment ..|> Strategy
    WalletPayment ..|> Strategy
  
Observer (Pub-Sub)
When state in one object (subject) changes, notify all dependents (observers). Subject doesn't know its observers directly — they subscribe.

Canonical use: news feed, event systems, UI updates.

classDiagram
    direction LR
    class Subject {
      +attach()
      +detach()
      +notify()
    }
    class Observer {
      +update()
    }
    class EmailNotifier
    class PushNotifier
    class AuditLogger
    Subject o-- Observer
    EmailNotifier ..|> Observer
    PushNotifier ..|> Observer
    AuditLogger ..|> Observer
  
State
An object changes behavior when its internal state changes. Each state is a class; transitions are explicit. Used for ATMs, vending machines, traffic lights, network protocols.
Command
Encapsulates a request as an object. Useful for undo/redo, queuing, logging actions.
Chain of Responsibility
Pass a request along a chain of handlers; each handler decides whether to handle or pass on. Used in logging frameworks (log levels), middleware (HTTP request pipelines).
Template Method
Defines the skeleton of an algorithm in a base class; subclasses fill in specific steps. Good when several flows share structure but differ in details.
Iterator
Provides a way to traverse a collection without exposing its internal structure. Most languages have this baked in (Java's Iterable, Python's __iter__).

When to use which pattern (the meta-skill)

Most patterns address a specific recurring problem:

ProblemPattern
Algorithm should be swappable at runtimeStrategy
Object's behavior depends on its stateState
One object's change needs to update manyObserver
Construction is complex, many fields, immutability desiredBuilder
Need a single instance globallySingleton (carefully)
Concrete class depends on runtime dataFactory
Tree structure with uniform handlingComposite
Add behaviors dynamicallyDecorator
Multiple handlers, sequential filteringChain of Responsibility
Encapsulate an action; support undoCommand