Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Observer Pattern

Stay updated automatically - when one object changes, all observers get notified!

Observer Pattern: Keeping Everyone in the Loop

Section titled “Observer Pattern: Keeping Everyone in the Loop”

Now let’s dive into the Observer Pattern - one of the most commonly used behavioral design patterns.

Imagine you’re subscribed to a YouTube channel. When the creator uploads a new video, you automatically get notified - you don’t have to keep checking! The Observer Pattern works the same way!

The Observer Pattern lets objects notify multiple other objects (observers) about state changes automatically. When the subject (the thing being watched) changes, all observers are notified without the subject needing to know who’s watching.

The Observer Pattern is useful when:

  1. One object changes and multiple objects need to be notified
  2. You want loose coupling - Subject doesn’t need to know about observers
  3. You need dynamic relationships - Observers can be added/removed at runtime
  4. You want to avoid polling - No need to constantly check for changes
  5. You need one-to-many dependency - One subject, many observers

What Happens If We Don’t Use Observer Pattern?

Section titled “What Happens If We Don’t Use Observer Pattern?”

Without the Observer Pattern, you might:

  • Tightly couple objects - Subject needs to know all observers
  • Use polling - Constantly checking for changes (inefficient)
  • Scatter notification logic - Update code spread everywhere
  • Make it hard to add/remove observers - Need to modify subject code
  • Violate Open/Closed Principle - Need to modify code to add observers

Let’s start with a super simple example that anyone can understand!

You’re building a weather monitoring system. When the temperature changes, you need to update multiple displays (phone app, website, billboard). Without Observer Pattern:

Problems:

  • WeatherStation knows about all display types (tight coupling)
  • Need to modify set_temperature() every time you add a new display
  • Hard to remove observers
  • Violates Open/Closed Principle

Problems:

  • WeatherStation knows about all display types (tight coupling)
  • Need to modify set_temperature() every time you add a new display
  • Hard to remove observers
  • Violates Open/Closed Principle
classDiagram
    class Subject {
        <<abstract>>
        +attach(observer) void
        +detach(observer) void
        +notify() void
    }
    class Observer {
        <<abstract>>
        +update(temperature) void
    }
    class WeatherStation {
        -temperature: float
        -observers: List~Observer~
        +attach(observer) void
        +detach(observer) void
        +notify() void
        +set_temperature(temp) void
        +get_temperature() float
    }
    class PhoneApp {
        +update(temperature) void
    }
    class Website {
        +update(temperature) void
    }
    class Billboard {
        +update(temperature) void
    }
    
    Subject <|-- WeatherStation : implements
    Observer <|-- PhoneApp : implements
    Observer <|-- Website : implements
    Observer <|-- Billboard : implements
    WeatherStation --> Observer : notifies
    
    note for WeatherStation "Maintains list of observers<br/>Notifies all on state change"
    note for Observer "All observers implement<br/>the same interface"
Diagram

Here’s how the Observer Pattern works in practice - showing the sequence of notifications:

sequenceDiagram
    participant Subject as WeatherStation
    participant Observer1 as PhoneApp
    participant Observer2 as Website
    participant Observer3 as Billboard
    
    Observer1->>Subject: attach(observer1)
    Observer2->>Subject: attach(observer2)
    Observer3->>Subject: attach(observer3)
    
    Note over Subject: Observers registered
    
    Subject->>Subject: set_temperature(25.0)
    Subject->>Subject: notify()
    
    Subject->>Observer1: update(25.0)
    activate Observer1
    Observer1-->>Subject: Display updated
    deactivate Observer1
    
    Subject->>Observer2: update(25.0)
    activate Observer2
    Observer2-->>Subject: Display updated
    deactivate Observer2
    
    Subject->>Observer3: update(25.0)
    activate Observer3
    Observer3-->>Subject: Display updated
    deactivate Observer3
    
    Note over Subject,Observer3: All observers notified<br/>automatically!

Real-World Software Example: E-Commerce Order System

Section titled “Real-World Software Example: E-Commerce Order System”

Now let’s see a realistic software example - an e-commerce system where multiple services need to be notified when an order is placed.

You’re building an e-commerce system. When an order is placed, you need to:

  • Send confirmation email
  • Update inventory
  • Process payment
  • Send notification to warehouse
  • Update analytics

Without Observer Pattern:

Problems:

  • Order class knows about all services (tight coupling)
  • Need to modify place_order() to add new services
  • Hard to test - need to mock all services
  • Violates Single Responsibility Principle
  • Can’t easily add/remove services at runtime
classDiagram
    class OrderSubject {
        <<abstract>>
        +attach(observer) void
        +detach(observer) void
        +notify() void
    }
    class OrderObserver {
        <<abstract>>
        +update(order) void
    }
    class Order {
        -orderId: str
        -items: List
        -amount: float
        -observers: List~OrderObserver~
        +attach(observer) void
        +detach(observer) void
        +notify() void
        +place_order() void
    }
    class EmailService {
        +update(order) void
        +send_confirmation(order) void
    }
    class InventoryService {
        +update(order) void
        +update_stock(order) void
    }
    class PaymentService {
        +update(order) void
        +process_payment(order) void
    }
    class WarehouseService {
        +update(order) void
        +prepare_shipment(order) void
    }
    class AnalyticsService {
        +update(order) void
        +record_order(order) void
    }
    
    OrderSubject <|-- Order : implements
    OrderObserver <|-- EmailService : implements
    OrderObserver <|-- InventoryService : implements
    OrderObserver <|-- PaymentService : implements
    OrderObserver <|-- WarehouseService : implements
    OrderObserver <|-- AnalyticsService : implements
    Order --> OrderObserver : notifies
    
    note for Order "When order is placed,<br/>all services notified automatically"
    note for OrderObserver "Each service handles<br/>its own responsibility"

With Observer Pattern, adding a new service is super easy:


There are several variations of the Observer Pattern:

Subject sends all data to observers:

Pros: Observers get all data
Cons: Observers might not need all data

Observers pull data they need from subject:

Pros: Observers get only what they need
Cons: Observers need to know subject’s interface

Using events instead of direct method calls:


Use Observer Pattern when:

One object changes and multiple objects need to be notified
You want loose coupling - Subject shouldn’t know about observers
You need dynamic relationships - Add/remove observers at runtime
You want to avoid polling - No constant checking for changes
You have one-to-many dependency - One subject, many observers
You’re following Open/Closed Principle - Open for extension, closed for modification

Don’t use Observer Pattern when:

Simple one-to-one communication - Direct method call is simpler
Performance is critical - Observer adds overhead (usually negligible)
Order of notifications matters - Observers might execute in unpredictable order
Observers need to modify subject - Can create circular dependencies
You have few, stable observers - Overhead might not be worth it


Mistake 3: Memory Leaks (Not Unsubscribing)

Section titled “Mistake 3: Memory Leaks (Not Unsubscribing)”

  1. Loose Coupling - Subject doesn’t depend on concrete observer classes
  2. Dynamic Relationships - Observers can be added/removed at runtime
  3. Open/Closed Principle - Easy to add observers without modifying subject
  4. Broadcast Communication - One notification reaches all observers
  5. No Polling - Observers don’t need to constantly check for changes
  6. Follows SOLID Principles - Especially Open/Closed and Dependency Inversion

Observer Pattern is a behavioral design pattern that defines a one-to-many dependency between objects. When one object (subject) changes state, all dependent objects (observers) are notified and updated automatically.

  • Loose coupling - Subject doesn’t know about specific observers
  • Dynamic subscription - Add/remove observers at runtime
  • Broadcast communication - One change notifies all observers
  • Avoid polling - No need to constantly check for changes
  • Follow Open/Closed Principle
  1. Define Observer interface - What all observers can do
  2. Define Subject interface - Methods to attach/detach/notify
  3. Create concrete Subject - Maintains list of observers
  4. Create concrete Observers - React to subject changes
  5. Subscribe observers - Attach observers to subject
  6. Notify on change - Subject notifies all observers when state changes
Subject → attach/detach → Observers
Subject → notify() → Observer.update()
  • Subject - The object being observed (has state)
  • Observer - Objects that watch the subject
  • attach() - Subscribe an observer
  • detach() - Unsubscribe an observer
  • notify() - Notify all observers
  • update() - Observer’s reaction method

✅ One-to-many dependency
✅ Subject changes and multiple objects need notification
✅ Want loose coupling
✅ Need dynamic add/remove observers
✅ Want to avoid polling

❌ Simple one-to-one communication
❌ Performance is critical
❌ Order of notifications matters
❌ Few, stable observers

  • Observer Pattern = One subject, many observers
  • Subject = Object being watched
  • Observer = Objects watching the subject
  • Benefit = Loose coupling, dynamic relationships
  • Principle = Open for extension, closed for modification
  • Observer Pattern decouples subject from observers
  • It enables dynamic relationships - add/remove at runtime
  • It follows Open/Closed Principle - easy to extend
  • Use it when you need one-to-many notification
  • Don’t use it for simple one-to-one cases - avoid over-engineering!

What to say:

“Observer Pattern is a behavioral design pattern that defines a one-to-many dependency between objects. When the subject (the object being observed) changes state, all its observers are automatically notified and updated.”

Why it matters:

  • Shows you understand the fundamental purpose
  • Demonstrates knowledge of behavioral patterns category
  • Indicates you can explain concepts clearly

Must mention:

  • One-to-many dependency - One subject, many observers
  • Loose coupling - Subject shouldn’t know about specific observers
  • Dynamic relationships - Add/remove observers at runtime
  • Avoid polling - No need to constantly check for changes
  • Event-driven architecture - React to events automatically

Example scenario to give:

“I’d use Observer Pattern when building a stock price monitoring system. When a stock price changes, multiple components need to react - the UI needs to update, alerts need to be sent, analytics need to be recorded. With Observer Pattern, the stock service just notifies all observers, and each handles the update differently.”

Must explain:

  1. Subject (Observable) - The object being observed, maintains list of observers
  2. Observer - Interface for objects that watch the subject
  3. Concrete Subject - Specific subject implementation
  4. Concrete Observer - Specific observer implementations
  5. attach() - Subscribe an observer
  6. detach() - Unsubscribe an observer
  7. notify() - Notify all observers
  8. update() - Observer’s reaction method

Visual explanation:

Subject → attach(observer) → Observer list
Subject → notify() → Observer.update()

Benefits to mention:

  • Loose Coupling - Subject doesn’t depend on concrete observer classes
  • Dynamic Relationships - Observers can be added/removed at runtime
  • Open/Closed Principle - Easy to add observers without modifying subject
  • Broadcast Communication - One notification reaches all observers
  • No Polling - Observers don’t need to constantly check

Trade-offs to acknowledge:

  • Performance - Notifying many observers can be slow
  • Order of execution - Observers might execute in unpredictable order
  • Memory leaks - Need to properly unsubscribe observers
  • Debugging difficulty - Hard to trace notification flow

Q: “What’s the difference between Observer Pattern and Pub-Sub Pattern?”

A:

“Observer Pattern has direct communication - the subject knows about observers and calls their update methods directly. Pub-Sub (Publish-Subscribe) Pattern uses a message broker - publishers and subscribers don’t know about each other, they communicate through a broker. Pub-Sub is more decoupled but adds complexity.”

Q: “How do you handle errors in Observer Pattern?”

A:

“I wrap each observer notification in a try-except block. If one observer fails, I log the error and continue notifying other observers. This ensures that one failing observer doesn’t prevent others from being notified. I might also implement a retry mechanism or dead-letter queue for critical observers.”

Q: “How does Observer Pattern relate to SOLID principles?”

A:

“Observer Pattern primarily supports the Open/Closed Principle - you can add new observers without modifying the subject. It also supports Dependency Inversion Principle by making subjects depend on the Observer abstraction rather than concrete observer classes. Additionally, it helps with Single Responsibility Principle by separating the subject’s core logic from notification logic.”

Key implementation points:

  1. Use Abstract Base Class (ABC) for observer interface

    from abc import ABC, abstractmethod
    class Observer(ABC):
    @abstractmethod
    def update(self, data): pass
  2. Maintain observer list - Use a list or set to store observers

    def __init__(self):
    self._observers: List[Observer] = []
  3. Handle duplicate subscriptions - Check if observer already exists

    def attach(self, observer: Observer):
    if observer not in self._observers:
    self._observers.append(observer)
  4. Error handling - Wrap notifications in try-except

    def notify(self):
    for observer in self._observers:
    try:
    observer.update(self._state)
    except Exception as e:
    logger.error(f"Error notifying {observer}: {e}")

Good examples to mention:

  • Model-View-Controller (MVC) - Model notifies views when data changes
  • Event-driven systems - UI events, button clicks, form submissions
  • Stock market - Stock prices notify multiple displays
  • Logging systems - Log events notify multiple handlers
  • Notification systems - Order placed notifies email, SMS, push services
  • GUI frameworks - Widgets notify listeners on events

Mistakes interviewers watch for:

  1. Memory leaks - Not unsubscribing observers

    • ❌ Bad: Attach observer, never detach
    • ✅ Good: Always detach when observer is no longer needed
  2. Observers modifying subject - Can cause infinite loops

    • ❌ Bad: Observer calls subject.set_state() in update()
    • ✅ Good: Observer only reacts, doesn’t modify subject
  3. No error handling - One failing observer stops all notifications

    • ❌ Bad: No try-except in notify()
    • ✅ Good: Wrap each notification in try-except
  4. Order dependencies - Assuming observers execute in specific order

    • ❌ Bad: Observer A depends on Observer B executing first
    • ✅ Good: Observers are independent

Observer vs Strategy:

  • Observer - One subject notifies many observers about changes
  • Strategy - One context uses one strategy at a time (algorithm selection)

Observer vs Mediator:

  • Observer - Direct communication, subject knows about observers
  • Mediator - Centralized communication through mediator

Observer vs Chain of Responsibility:

  • Observer - All observers get notified
  • Chain of Responsibility - Request passes through chain until handled

What interviewers look for:

Clean code - Readable, well-structured
Type hints - Proper type annotations
Error handling - Handle observer failures gracefully
Memory management - Proper cleanup, avoid leaks
SOLID principles - Follows design principles
Testability - Easy to test and mock

Example of good code:

Before your interview, make sure you can:

  • Define Observer Pattern clearly in one sentence
  • Explain when to use it (with examples)
  • Describe the structure and components
  • List benefits and trade-offs
  • Compare with other behavioral patterns
  • Implement Observer Pattern from scratch
  • Handle errors and memory leaks
  • Connect to SOLID principles
  • Identify when NOT to use it
  • Give 2-3 real-world examples
  • Discuss common mistakes and how to avoid them

Remember: Observer Pattern is about keeping objects informed automatically - when one object changes, all observers get notified without tight coupling! 👀