LLD Hub
design-patternslldinterview

Top 10 Design Patterns for LLD Interviews | Complete Guide

Strategy, Observer, Factory, State, Command, Decorator — the 10 design patterns that appear in 90% of LLD interview questions. With real examples.

22 April 2025·12 min read

Design patterns are the vocabulary of LLD interviews. At Amazon, Flipkart, Swiggy, and Google, 90% of LLD problems require one or more of the same 10 patterns. Knowing which pattern to apply — and why — is what separates good answers from great ones. This guide covers all 10 with real LLD examples, Java code, and when to use each.

Why Design Patterns Matter in LLD Interviews

An interviewer does not want to see a solution that works. They want to see a solution that is extensible, testable, and well-reasoned. Design patterns give you language to justify your decisions: "I used Strategy here because the pricing algorithm needs to be swappable without changing ParkingLot." That one sentence shows OCP awareness, pattern knowledge, and system thinking simultaneously.

1. Strategy Pattern

Define a family of algorithms, encapsulate each, and make them interchangeable. Use when behavior varies and you want to swap it without changing the class that uses it.

public interface PricingStrategy {
    double calculate(Ticket ticket, LocalDateTime exitTime);
}
public class HourlyPricing implements PricingStrategy { ... }
public class FlatRatePricing implements PricingStrategy { ... }

// ParkingLot uses PricingStrategy — swappable at construction
public class ParkingLot {
    private final PricingStrategy pricing;
    public Payment unpark(String ticketId) {
        double fee = pricing.calculate(ticket, LocalDateTime.now());
        ...
    }
}

Where it appears: Parking Lot (pricing), Splitwise (split types), Rate Limiter (algorithms), Ride Sharing (fare calculation), Notification System (per-channel delivery).

2. Observer Pattern

Define a one-to-many dependency — when one object changes state, all dependents are notified automatically. Use for real-time updates, event-driven systems, and decoupled notifications.

public interface OrderObserver {
    void onStatusChange(Order order, OrderStatus newStatus);
}
public class PushNotificationObserver implements OrderObserver { ... }
public class AnalyticsObserver implements OrderObserver { ... }

public class OrderService {
    private final List<OrderObserver> observers = new ArrayList<>();
    public void updateStatus(Order order, OrderStatus status) {
        order.setStatus(status);
        observers.forEach(obs -> obs.onStatusChange(order, status));
    }
}

Where it appears: Food Delivery (order updates), Chat App (message delivery), Social Feed (new post notifications), Logger (handlers), Inventory (low-stock alerts).

3. Factory Pattern

Create objects without specifying the exact class. Use when the type of object to create depends on runtime data and you want to centralize creation logic.

public class VehicleFactory {
    public static Vehicle create(String type, String licensePlate) {
        return switch (type.toUpperCase()) {
            case "BIKE"  -> new Bike(licensePlate);
            case "CAR"   -> new Car(licensePlate);
            case "TRUCK" -> new Truck(licensePlate);
            default      -> throw new IllegalArgumentException("Unknown type: " + type);
        };
    }
}

Where it appears: Parking Lot (vehicle creation), Ride Sharing (ride type), Job Scheduler (command creation), Notification (channel creation).

4. Singleton Pattern

Ensure only one instance of a class exists and provide a global access point. Use for shared resources (config, connection pools, loggers) that must be consistent across the application.

public class ParkingLot {
    private static volatile ParkingLot instance;

    private ParkingLot() {}

    public static ParkingLot getInstance() {
        if (instance == null) {
            synchronized (ParkingLot.class) {
                if (instance == null) instance = new ParkingLot();
            }
        }
        return instance;
    }
}

Where it appears: Parking Lot, Logger, Database connection pool, Cache manager.

5. State Pattern

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. Use for lifecycle management where behavior depends on current state.

public interface ElevatorState {
    void handleRequest(Elevator e, int floor);
    void move(Elevator e);
}
public class IdleState    implements ElevatorState { ... }
public class MovingUpState implements ElevatorState { ... }

public class Elevator {
    private ElevatorState state = new IdleState();
    public void setState(ElevatorState state) { this.state = state; }
    public void move() { state.move(this); } // delegates to current state
}

Where it appears: Elevator System (elevator states), ATM Machine (card inserted, authenticated), Vending Machine, Traffic Light.

6. Command Pattern

Encapsulate a request as an object. Use when you need to parameterize actions, queue them, log them, or support undo operations.

public interface StockMovement {
    void execute();
    MovementType getType();
}
public class StockReceiveMovement implements StockMovement { ... }
public class StockDeductMovement  implements StockMovement { ... }

// InventoryService calls execute() and logs the command type
public void executeAndAudit(StockMovement movement) {
    movement.execute();
    auditLog.record(movement.getType(), ...);
}

Where it appears: Inventory Management (stock movements), Job Scheduler (job commands), Text Editor (undo/redo), Elevator (floor requests).

7. Chain of Responsibility Pattern

Pass a request along a chain of handlers until one handles it (or all do). Use for ordered processing pipelines where each step decides to handle, modify, or pass the request.

public abstract class PaymentHandler {
    protected PaymentHandler next;
    public abstract PaymentResult handle(Payment p);
    protected PaymentResult proceed(Payment p) {
        return next != null ? next.handle(p) : PaymentResult.proceed();
    }
}
public class FraudCheckHandler extends PaymentHandler { ... }
public class LimitCheckHandler extends PaymentHandler { ... }

// Wire the chain
fraudHandler.setNext(limitHandler).setNext(authHandler);
PaymentResult result = fraudHandler.handle(payment);

Where it appears: Payment Gateway (pre-payment checks), Logger (handlers), ATM (cash dispensing notes), API middleware.

8. Decorator Pattern

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing. Use when you want to add behavior without modifying the base class.

public class LoyaltyPricing implements PricingStrategy {
    private final PricingStrategy base; // wraps another strategy

    public LoyaltyPricing(PricingStrategy base, double discount) {
        this.base = base;
        this.discount = discount;
    }

    @Override
    public double calculatePrice(Room room, LocalDate checkIn, LocalDate checkOut) {
        return base.calculatePrice(room, checkIn, checkOut) * (1 - discount);
    }
}

Where it appears: Hotel Booking (pricing layers), Logger (formatters), I/O streams (Java's own BufferedInputStream wraps InputStream).

9. Template Method Pattern

Define the skeleton of an algorithm in a base class, deferring some steps to subclasses. Use when multiple classes share the same algorithm structure with different details.

public abstract class ReportGenerator {
    // Template method — defines the algorithm skeleton
    public final Report generate(String period) {
        Data data = fetchData(period);   // step 1 — same for all
        Data processed = process(data);  // step 2 — varies per subclass
        return format(processed);        // step 3 — varies per subclass
    }

    protected abstract Data process(Data raw);
    protected abstract Report format(Data processed);
    private Data fetchData(String period) { ... } // shared
}

public class SalesReport extends ReportGenerator { ... }
public class InventoryReport extends ReportGenerator { ... }

Where it appears: Notification System (send flow per channel), Report generation, Data import pipelines.

10. Repository Pattern

Encapsulate data access logic behind a collection-like interface. Use to decouple business logic from persistence concerns and to make services testable with mock repositories.

public interface BookRepository {
    Optional<Book> findByIsbn(String isbn);
    List<Book> findByAuthor(String author);
    void save(Book book);
}

public class SqlBookRepository implements BookRepository { ... }     // production
public class InMemoryBookRepository implements BookRepository { ... } // tests

// LibraryService depends on the interface, not the implementation
public class LibraryService {
    private final BookRepository bookRepo;
    public LibraryService(BookRepository repo) { this.bookRepo = repo; }
}

Where it appears: Every LLD problem — BookRepository, OrderRepository, TicketRepository, UserRepository.

Pattern Selection Quick Guide

Problem Symptom                      | Use This Pattern
------------------------------------ | ----------------
Multiple algorithms, same interface  | Strategy
React to events across classes       | Observer
Create objects based on runtime type | Factory
One instance across entire app       | Singleton
Object behavior changes with state   | State
Log, queue, or undo an operation     | Command
Sequential processing pipeline       | Chain of Responsibility
Add behavior without changing class  | Decorator
Same algorithm, different details    | Template Method
Decouple DB access from business     | Repository

FAQ — Design Patterns for LLD Interviews

Which design pattern is most commonly asked in LLD interviews?

Strategy pattern appears in almost every LLD problem — pricing, split types, rate limiting algorithms, assignment strategies, notification channels. If you master one pattern for LLD interviews, make it Strategy. Observer is a close second, appearing wherever real-time updates or event notifications are required.

What is the difference between Strategy and Template Method?

Strategy uses composition — the varying algorithm is a separate object injected into the context. Template Method uses inheritance — the varying steps are abstract methods in the base class. Strategy is more flexible (swap at runtime) and more testable (mock the strategy). Template Method is simpler but couples the variant to the base class through inheritance.

When should you use Factory vs Constructor directly?

Use a Factory when: the type of object to create depends on runtime data, creation involves complex logic, or you want to centralize creation to make future changes easier. Use a constructor directly for simple objects where the caller knows the exact type and there is no creation complexity.

How do you explain the Chain of Responsibility to an interviewer?

"I have a sequence of checks that must run in order before processing a payment. Each check is independent — fraud check does not know about limit check. If any check rejects the payment, it short-circuits. Chain of Responsibility lets me add, remove, or reorder checks without changing the payment processing logic."

Ready to practice?

Submit your solution and get AI-scored feedback on OOP, SOLID principles, design patterns, and code quality.

Browse All Problems →