CacheU
Low Level Design

Observer Design Pattern

A detailed guide to the Observer pattern, including its purpose, working model, polling vs push, subject-observer roles, real-world examples, benefits, drawbacks, and implementation in C++, Java, and Python.

Observer Design Pattern

Have you ever subscribed to a YouTube channel?

When the creator uploads a new video, you get a notification automatically. You do not need to keep checking the channel every few minutes. The update comes to you.

That everyday experience is a perfect real-world example of the Observer Design Pattern.

The Observer Pattern solves a fundamental software problem:

How can one object efficiently notify many other dependent objects when something changes?


Introduction: The Core Problem

In many systems, one object changes state and a group of other objects needs to react immediately.

Examples:

  • a YouTube channel uploads a new video
  • a stock price changes
  • a button is clicked
  • a weather station records a new temperature
  • an order status changes from Placed to Shipped

Without a proper pattern, this often leads to repeated checking.

This is called polling.


Polling vs Push

Polling

The observer repeatedly checks whether something changed.

Push

The subject sends a notification only when something changes.

Diagram
flowchart LR A[Polling] --> B[Observer keeps asking] B --> C[Is there an update?] C --> B B --> D[Wasteful and repetitive] E[Push] --> F[Subject changes state] F --> G[Subject notifies observers] G --> H[Efficient and event-driven]

Why polling is bad

Polling wastes time and resources.

ProblemResult
Repeated requestsNetwork and CPU waste
No real updateSame answer repeated again and again
Poor scalabilityToo many unnecessary checks
Inefficient designSystem works harder than needed

Why push is better

Push-based systems notify only when needed.

BenefitResult
Less wasted workCommunication happens only on change
Better scalabilityMany observers can be notified efficiently
Cleaner designResponsibility is clearly separated
Event-driven behaviorReact when an event actually occurs

What is the Observer Pattern?

The Observer Pattern defines a one-to-many relationship between objects so that when one object changes state, all of its dependents are notified and updated automatically.


Main participants

There are two key roles:

RoleMeaningExample
SubjectThe object being watchedYouTube channel
ObserverThe object watching the subjectSubscribers
Diagram
classDiagram class Subject { +subscribe +unsubscribe +notify } class Observer { +update } class ConcreteSubject { +subscribe +unsubscribe +notify +getState } class ConcreteObserver { +update } Subject <|-- ConcreteSubject Observer <|-- ConcreteObserver ConcreteSubject --> Observer

Key idea

The Subject does not need to know the exact details of every Observer.

It only needs to know:

  • who is subscribed
  • how to notify them
  • when to notify them

This creates loose coupling.


YouTube analogy

Imagine a YouTube channel.

  • The channel is the Subject
  • Subscribers are the Observers

When the channel uploads a new video:

  1. subscribers are registered
  2. a state change happens
  3. the channel notifies everyone
  4. each subscriber reacts

Why this pattern is useful

The Observer Pattern is useful because it provides:

  • automatic updates
  • loose coupling
  • easy extensibility
  • event-driven design
  • clean separation of concerns

Core responsibilities

Subject responsibilities

The Subject usually provides:

  • subscribe(observer)
  • unsubscribe(observer)
  • notify()

What these do

  • subscribe adds an observer
  • unsubscribe removes an observer
  • notify informs all observers that something changed

Observer responsibilities

The Observer usually provides:

  • update()

What this does

The observer reacts to the notification.

It may:

  • fetch new data
  • refresh UI
  • log an event
  • trigger another process

Observer workflow

Diagram
flowchart TD A[Observer Subscribes] --> B[Subject Stores Observer] B --> C[Subject State Changes] C --> D[Subject Calls Notify] D --> E[Loop Through Observers] E --> F[Call Update On Each Observer] F --> G[Observers React]

A complete example: YouTube channel

Suppose a channel uploads a new video.

Subject

  • YouTubeChannel

Observers

  • User1
  • User2
  • User3

When a new video is published, each user is notified.


Sequence diagram

Diagram
sequenceDiagram actor Creator participant Channel participant User1 participant User2 participant User3 Creator->>Channel: uploadVideo("Observer Pattern Tutorial") Channel->>Channel: notify() Channel->>User1: update() Channel->>User2: update() Channel->>User3: update() User1-->>Channel: fetch new video info User2-->>Channel: fetch new video info User3-->>Channel: fetch new video info

Push model with pull of details

A very important idea in Observer is this:

  • the Subject pushes a notification
  • the Observer may then pull the exact data it needs

This keeps the notification lightweight.


Observer structure

Diagram
classDiagram class Subject { subscribe unsubscribe notify } class ConcreteSubject { observers state subscribe unsubscribe notify getState setState } class Observer { update } class ConcreteObserver { subject update } Subject <|-- ConcreteSubject Observer <|-- ConcreteObserver ConcreteSubject --> Observer ConcreteObserver --> ConcreteSubject

Real-world use cases

The Observer Pattern is everywhere.

Use caseSubjectObservers
YouTube notificationsChannelSubscribers
Social media feedsUser profileFollowers
GUI eventsButtonEvent listeners
Stock market appsStock priceTraders / dashboards
Weather stationsSensorDisplays / alerts
Order trackingOrderCustomer notifications

Example 1: Stock market app

A stock price changes many times during the day.

The system may have:

  • one stock feed
  • many dashboards
  • many alert services

When the price changes, all observers are notified.

Diagram
flowchart TD A[Stock Price Update] --> B[Stock Subject] B --> C[Dashboard 1] B --> D[Dashboard 2] B --> E[Alert Service] B --> F[Trading Bot]

Example 2: GUI button

A button can have multiple listeners.

When the button is clicked:

  • logger runs
  • animation starts
  • analytics event is recorded
  • form submission occurs

The button does not need to know what each listener does.


Why Observer supports loose coupling

Observer is powerful because the Subject and Observers are not tightly tied together.

Design aspectResult
Subject does not know observer detailsEasier to extend
Observers do not depend on internal subject logicCleaner design
New observers can be added easilyMore flexible system
Observers can be removed anytimeBetter runtime control

Observer and SRP

Sometimes the Subject also handles notification logic.

At first glance, that may seem like multiple responsibilities.

But in practice, this is often acceptable because:

  • core business logic and notification logic are both stable
  • notification handling is generic
  • the pattern keeps the system practical and simple

Still, in larger systems, notification responsibility can be separated if needed.


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
 
class Observer {
public:
    virtual void update(const string& videoTitle) = 0;
    virtual ~Observer() = default;
};
 
class Subject {
public:
    virtual void subscribe(Observer* observer) = 0;
    virtual void unsubscribe(Observer* observer) = 0;
    virtual void notify() = 0;
    virtual ~Subject() = default;
};
 
class YouTubeChannel : public Subject {
private:
    vector<Observer*> observers;
    string latestVideo;
 
public:
    void subscribe(Observer* observer) override {
        observers.push_back(observer);
    }
 
    void unsubscribe(Observer* observer) override {
        observers.erase(remove(observers.begin(), observers.end(), observer), observers.end());
    }
 
    void notify() override {
        for (Observer* observer : observers) {
            observer->update(latestVideo);
        }
    }
 
    void uploadVideo(const string& title) {
        latestVideo = title;
        cout << "New video uploaded: " << title << endl;
        notify();
    }
 
    string getLatestVideo() const {
        return latestVideo;
    }
};
 
class Subscriber : public Observer {
private:
    string name;
    YouTubeChannel* channel;
 
public:
    Subscriber(const string& subscriberName, YouTubeChannel* ch)
        : name(subscriberName), channel(ch) {}
 
    void update(const string& videoTitle) override {
        cout << name << " received notification: " << videoTitle << endl;
    }
};
 
int main() {
    YouTubeChannel channel;
 
    Subscriber s1("Alice", &channel);
    Subscriber s2("Bob", &channel);
 
    channel.subscribe(&s1);
    channel.subscribe(&s2);
 
    channel.uploadVideo("Observer Pattern Tutorial");
 
    channel.unsubscribe(&s1);
 
    channel.uploadVideo("Factory Pattern Tutorial");
 
    return 0;
}

How the implementation works

Step 1: Observer subscribes

A subscriber joins the channel.

Step 2: Subject stores observer

The channel keeps a list of observers.

Step 3: State changes

The channel uploads a new video.

Step 4: Subject notifies observers

The channel calls notify().

Step 5: Observers update themselves

Each observer reacts to the update.


A more detailed Observer diagram

Diagram
classDiagram class YouTubeChannel { -List observers -String latestVideo +subscribe() +unsubscribe() +notify() +uploadVideo() +getLatestVideo() } class Subscriber { -String name +update() } YouTubeChannel --> Subscriber

Why Observer is powerful

1. Easy to extend

You can add a new observer without changing the subject.

Example:

  • add a mobile app notification observer
  • add an email notification observer
  • add a logging observer

The subject does not need to know the details.


2. Easy to remove observers

Observers can unsubscribe at runtime.

This is useful in:

  • live feeds
  • event listeners
  • notification systems

3. Supports dynamic systems

The subject can have:

  • zero observers
  • one observer
  • many observers

The system adjusts dynamically.


Observer and event-driven programming

Observer is one of the most important patterns behind event-driven systems.

It appears in:

  • UI frameworks
  • reactive systems
  • message systems
  • notification services

Whenever you see a “listener” or “subscriber,” there is often an Observer-style design behind it.


Observer vs polling

FeaturePollingObserver
Who checks?Observer checks repeatedlySubject notifies automatically
EfficiencyLowHigh
Resource usageWastes resourcesUses resources only when needed
Design stylePull-basedPush-based
ScalabilityPoorerBetter

Observer vs Publish-Subscribe

These are closely related concepts.

ConceptMeaning
ObserverOne subject directly notifies observers
Publish-SubscribePublishers send messages to a broker, which distributes them to subscribers

Observer is often more direct, while publish-subscribe is more decoupled and often used in distributed systems.


Benefits of Observer Pattern

BenefitDescription
Loose couplingSubject and observers remain independent
Easy notificationUpdates are sent automatically
Scalable designMany observers can be supported
Flexible architectureObservers can be added or removed dynamically
Clean separationSubject handles state, observers handle reaction

Drawbacks of Observer Pattern

DrawbackDescription
Notification overheadToo many observers can increase cost
Debugging difficultyChains of updates may be hard to trace
Unexpected updatesOne change can trigger many reactions
Ordering complexityIf many observers exist, notification order may matter

Common mistakes

MistakeProblem
Forgetting to unsubscribeMemory leaks or stale listeners
Making observers too dependent on subject internalsBreaks loose coupling
Triggering expensive logic inside `notify()`Slows the system
Using observer when only one response is neededOverengineering
Not handling observer errorsOne bad observer may affect the flow

When to use Observer Pattern

Use it when:

  • one object changes and many others need to react
  • you want event-driven design
  • the subject should not know observer details
  • subscribers may join or leave dynamically
  • you want loose coupling between state and reactions

When not to use Observer Pattern

Avoid it when:

  • there is only one dependent object
  • updates are rare and simple
  • the extra abstraction adds unnecessary complexity
  • the communication is better handled directly

Real-world examples

DomainSubjectObserver
YouTubeChannelSubscriber
Social mediaUser postFollowers
GUIButtonEvent listeners
Trading appStock priceChart / alerts
Weather appSensorDisplay widgets
Order systemOrder statusNotification service

Summary

The Observer Pattern defines a one-to-many relationship where one object changes and many other objects are notified automatically.

It is a classic solution to the problem of keeping dependent objects updated without using inefficient polling.

The main ideas are:

  • the Subject manages state and observers
  • the Observer reacts through update()
  • the system stays loosely coupled
  • communication happens only when needed

Final takeaway

The Observer Pattern is all about this simple idea:

“When one object changes, let the interested objects know automatically.”

That makes it ideal for:

  • notifications
  • event systems
  • UI listeners
  • live data updates
  • reactive architecture

It is one of the most practical and widely used design patterns in software development.