Bird
Raised Fist0
LLDsystem_design~7 mins

When to use which behavioral pattern in LLD - System Design Guide

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Problem Statement
Without clear guidance on when to use each behavioral design pattern, developers often misuse or overcomplicate their code. This leads to tangled logic, poor maintainability, and difficulty adapting to changing requirements.
Solution
Each behavioral pattern addresses a specific kind of communication or responsibility delegation between objects. By understanding the problem each pattern solves, developers can choose the right one to organize interactions cleanly and flexibly, improving code clarity and adaptability.
Architecture
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│   Client      │──────▶│ Behavioral    │──────▶│ Receiver/     │
│ (Caller)      │       │ Pattern       │       │ Handler       │
└───────────────┘       └───────────────┘       └───────────────┘

Patterns like Chain of Responsibility pass requests along a chain until handled.
Patterns like Observer notify multiple dependents about state changes.
Strategy encapsulates interchangeable algorithms used by the client.

This diagram shows how behavioral patterns mediate communication between clients and receivers or handlers, organizing responsibilities and interactions.

Trade-offs
✓ Pros
Improves code flexibility by decoupling sender and receiver objects.
Enhances maintainability by localizing behavior changes to specific pattern classes.
Facilitates reuse of common interaction logic across different parts of the system.
Supports easier testing by isolating behaviors.
✗ Cons
Adds complexity by introducing additional classes and interfaces.
May lead to over-engineering if used without clear need.
Can make the flow of control harder to follow for beginners.
Use when your system has complex object interactions that need clear responsibility delegation, such as event handling, request routing, or algorithm selection. Typically beneficial when you have multiple ways to handle a request or when behavior changes dynamically.
Avoid when interactions are simple and direct, or when adding patterns would unnecessarily complicate the design without clear benefits, such as in small, straightforward applications with minimal behavior variation.
Real World Examples
Netflix
Uses the Strategy pattern to switch between different video encoding algorithms dynamically based on device capabilities.
Amazon
Applies the Observer pattern to update multiple inventory and pricing systems when product data changes.
LinkedIn
Implements Chain of Responsibility to process user requests through a series of validation and authorization steps.
Code Example
The before code mixes payment methods inside one class with conditionals, making it hard to extend. The after code uses the Strategy pattern to encapsulate each payment method in its own class, allowing PaymentProcessor to delegate payment without knowing details. This improves flexibility and maintainability.
LLD
### Before: No pattern, direct conditional logic
class PaymentProcessor:
    def pay(self, method, amount):
        if method == 'credit':
            print(f"Processing credit card payment of {amount}")
        elif method == 'paypal':
            print(f"Processing PayPal payment of {amount}")
        else:
            print("Unknown payment method")

### After: Strategy pattern applied
from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing credit card payment of {amount}")

class PayPalPayment(PaymentStrategy):
    def pay(self, amount):
        print(f"Processing PayPal payment of {amount}")

class PaymentProcessor:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def pay(self, amount):
        self.strategy.pay(amount)

# Usage
processor = PaymentProcessor(CreditCardPayment())
processor.pay(100)
processor.strategy = PayPalPayment()
processor.pay(200)
OutputSuccess
Alternatives
Command
Encapsulates a request as an object, allowing parameterization and queuing, rather than focusing on object interaction flow.
Use when: Use when you need to queue, log, or undo operations rather than just delegate behavior.
Mediator
Centralizes complex communication between objects into a mediator object, instead of distributing responsibility among them.
Use when: Choose when many objects interact in complex ways and you want to reduce direct dependencies.
Summary
Behavioral patterns organize how objects communicate and delegate responsibilities.
Choosing the right pattern depends on the interaction complexity and flexibility needs.
Using these patterns improves code maintainability but adds some complexity.

Practice

(1/5)
1. Which behavioral pattern is best suited when you want multiple objects to be notified automatically when one object changes its state?
easy
A. Observer pattern
B. Strategy pattern
C. Command pattern
D. Chain of Responsibility pattern

Solution

  1. Step 1: Understand the need for automatic notifications

    The problem requires multiple objects to be updated when one object changes state, which means a one-to-many dependency.
  2. Step 2: Match the pattern to the problem

    The Observer pattern is designed exactly for this: it lets observers subscribe to an object and get notified on changes.
  3. Final Answer:

    Observer pattern -> Option A
  4. Quick Check:

    Change notification = Observer [OK]
Hint: Notifications to many? Use Observer pattern [OK]
Common Mistakes:
  • Confusing Strategy with Observer
  • Using Command for notifications
  • Choosing Chain of Responsibility for updates
2. Which pattern allows you to change an object's behavior at runtime by switching between different algorithms or strategies?
easy
A. Observer pattern
B. Strategy pattern
C. Command pattern
D. Chain of Responsibility pattern

Solution

  1. Step 1: Identify the need for interchangeable behaviors

    The question asks about changing behavior dynamically, which means selecting algorithms or methods at runtime.
  2. Step 2: Select the pattern that supports behavior switching

    The Strategy pattern encapsulates algorithms and lets you swap them easily without changing the client code.
  3. Final Answer:

    Strategy pattern -> Option B
  4. Quick Check:

    Change behavior dynamically = Strategy [OK]
Hint: Switch algorithms easily? Use Strategy pattern [OK]
Common Mistakes:
  • Mixing Strategy with Observer
  • Using Command for behavior changes
  • Choosing Chain of Responsibility incorrectly
3. Consider this scenario: You have a request that can be handled by multiple objects in a chain. Each object decides if it can handle the request or passes it on. Which pattern fits this design?
Request -> Handler1 -> Handler2 -> Handler3
medium
A. Command pattern
B. Strategy pattern
C. Observer pattern
D. Chain of Responsibility pattern

Solution

  1. Step 1: Analyze the request handling flow

    The request passes through a chain of handlers, each deciding to handle or forward it.
  2. Step 2: Identify the matching behavioral pattern

    The Chain of Responsibility pattern allows multiple objects to handle a request in sequence until one handles it.
  3. Final Answer:

    Chain of Responsibility pattern -> Option D
  4. Quick Check:

    Request passes chain = Chain of Responsibility [OK]
Hint: Request passes chain? Use Chain of Responsibility [OK]
Common Mistakes:
  • Confusing Chain with Command
  • Using Observer for request handling
  • Choosing Strategy incorrectly
4. You have a system where commands need to be queued, logged, and executed later. Which behavioral pattern should you use? Identify the error in this choice:
Using Observer pattern to queue commands.
medium
A. Incorrect, use Command pattern instead
B. Incorrect, use Chain of Responsibility instead
C. Incorrect, use Strategy pattern instead
D. Correct use of Observer

Solution

  1. Step 1: Understand the requirement for queuing and executing commands

    Queuing, logging, and executing commands later requires encapsulating requests as objects.
  2. Step 2: Identify the pattern that encapsulates requests

    The Command pattern encapsulates requests as objects, allowing queuing and deferred execution.
  3. Step 3: Identify the error in using Observer

    Observer is for notifications, not for command encapsulation or queuing.
  4. Final Answer:

    Incorrect, use Command pattern instead -> Option A
  5. Quick Check:

    Queue commands = Command pattern [OK]
Hint: Queue commands? Use Command, not Observer [OK]
Common Mistakes:
  • Using Observer for command queuing
  • Confusing Command with Strategy
  • Choosing Chain of Responsibility wrongly
5. You are designing a notification system where users can subscribe to different event types, and the system should allow adding new event types without changing existing code. Which combination of behavioral patterns is best suited?
hard
A. Chain of Responsibility for subscriptions and Command for event handling
B. Command for subscriptions and Chain of Responsibility for event handling
C. Observer for subscriptions and Strategy for event handling
D. Strategy for subscriptions and Observer for event handling

Solution

  1. Step 1: Identify the subscription mechanism

    Users subscribing to events fits the Observer pattern, which supports dynamic subscription and notification.
  2. Step 2: Identify flexible event handling

    Strategy pattern allows interchangeable algorithms for handling different event types without changing existing code.
  3. Step 3: Combine patterns for extensibility

    Using Observer for subscriptions and Strategy for event handling supports adding new event types easily and keeps code maintainable.
  4. Final Answer:

    Observer for subscriptions and Strategy for event handling -> Option C
  5. Quick Check:

    Subscribe = Observer, flexible handling = Strategy [OK]
Hint: Subscribe = Observer, flexible handling = Strategy [OK]
Common Mistakes:
  • Mixing Command with subscriptions
  • Using Chain of Responsibility for subscriptions
  • Confusing Strategy with Observer roles