Define a one-to-many dependency so that when one object changes state, all its dependents are notified and updated automatically.
The Observer pattern is a behavioral design pattern where an object (called the Subject or Publisher) maintains a list of dependents (called Observers or Subscribers) and notifies them automatically whenever its state changes.
It establishes a one-to-many relationship between objects: one subject, many observers. When the subject changes, every observer is instantly informed without the subject needing to know anything specific about them.
This pattern is also known as the Publish-Subscribe (Pub/Sub) pattern. Think of a YouTube channel: you (observer) subscribe to a channel (subject). Whenever a new video is uploaded (state change), you get a notification — without the creator knowing who you personally are.
Key Idea: Objects subscribe to another object's events and react to changes — without tight coupling between them.
The object that holds state and triggers notifications. It maintains a list of observers, and exposes methods to register, remove, and notify them. It doesn't care who or how many observers are listening.
The interface all concrete observers must implement. It typically defines a single update() method that the subject calls when notifying. This ensures all observers speak the same language.
The real implementation of the subject. It holds the actual state (e.g., stock price, user data) and calls notifyObservers() whenever that state changes.
The actual subscriber. Implements the Observer interface and defines its own reaction to a notification — e.g., sending an email, updating a UI element, writing a log entry.
Without the Observer pattern, if Object A changes and Object B, C, and D need to know about it, Object A has to call each of them directly. This creates hard dependencies — every new consumer means modifying the source object.
// Problem: Subject knows about every consumer (tight coupling)
class StockMarket {
void setPrice(double price) {
this.price = price;
// Subject must call EVERY dependent manually
mobileApp.onPriceChange(price); // Coupled
emailSystem.sendAlert(price); // Coupled
dashboardUI.refresh(price); // Coupled
analyticsService.track(price); // Coupled
// Adding new consumer = edit this class!
}
}// Solution: Subject only knows about the Observer interface
class StockMarket {
void setPrice(double price) {
this.price = price;
notifyObservers(); // That's it — loosely coupled!
}
// Adding a new consumer = zero changes to this class
}All observers implement a common interface with an update() method so the subject can call them uniformly.
The subject holds a list of observer references. It exposes subscribe() and unsubscribe() methods.
When the subject's state changes, it loops through the list and calls update() on each registered observer.
Each observer handles the notification in its own way — send email, update UI, log data — completely decoupled from each other.
Notification Flow
There are two common ways for the subject to communicate data to its observers:
The subject sends all relevant data directly in the update() call. Observers receive everything they need upfront.
// Subject pushes data observer.update(stockName, price, volume);
✓ Simple and fast for observers
✗ Subject must know what data observers need
The subject sends a reference to itself. Observers pull whatever data they specifically need.
// Subject passes itself observer.update(this); // Observer pulls what it needs double price = subject.getPrice();
✓ Flexible — observers take only what they need
✗ Observers are coupled to subject's interface
// Observer Pattern — Stock Price Notifier
import java.util.ArrayList;
import java.util.List;
// Observer interface
interface StockObserver {
void update(String stockName, double price);
}
// Subject interface
interface StockSubject {
void registerObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
// Concrete Subject
public class StockMarket implements StockSubject {
private List<StockObserver> observers = new ArrayList<>();
private String stockName;
private double stockPrice;
public StockMarket(String stockName) {
this.stockName = stockName;
}
@Override
public void registerObserver(StockObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(StockObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (StockObserver observer : observers) {
observer.update(stockName, stockPrice);
}
}
// When price changes, notify all observers
public void setStockPrice(double price) {
this.stockPrice = price;
notifyObservers();
}
}
// Concrete Observers
class MobileApp implements StockObserver {
private String appName;
public MobileApp(String appName) {
this.appName = appName;
}
@Override
public void update(String stockName, double price) {
System.out.println("[" + appName + "] Push notification: "
+ stockName + " is now ₹" + price);
}
}
class EmailAlertSystem implements StockObserver {
@Override
public void update(String stockName, double price) {
System.out.println("[Email] Alert sent: "
+ stockName + " price updated to ₹" + price);
}
}
// Usage Example
public class Application {
public static void main(String[] args) {
StockMarket tataStock = new StockMarket("TATA");
StockObserver app1 = new MobileApp("Zerodha");
StockObserver app2 = new MobileApp("Groww");
StockObserver emailAlert = new EmailAlertSystem();
tataStock.registerObserver(app1);
tataStock.registerObserver(app2);
tataStock.registerObserver(emailAlert);
tataStock.setStockPrice(3450.75);
// Output:
// [Zerodha] Push notification: TATA is now ₹3450.75
// [Groww] Push notification: TATA is now ₹3450.75
// [Email] Alert sent: TATA price updated to ₹3450.75
// Remove an observer
tataStock.removeObserver(emailAlert);
tataStock.setStockPrice(3480.00);
// Only Zerodha and Groww are notified now
}
}When UI components need to react to data changes — like re-rendering a chart when underlying data updates.
React state, DOM events, ReduxWhen multiple services need to be notified about a change — like email, SMS, and push notifications all triggered by one event.
EmailAlert, SMSService, PushNotificationIn MVC architecture, the View observes the Model. When the Model updates, all Views automatically re-render.
MVC, MVVM, MVP patternsWhen various monitoring tools need to track system events without being embedded in business logic.
AuditLogger, MetricsCollector, DebugTracerLive stock prices, sports scores, chat messages — anything where multiple consumers need the same live updates.
StockTicker, LiveScoreboard, ChatRoom| Aspect | Observer | Pub/Sub |
|---|---|---|
| Coupling | Subject knows about observers | Publisher and subscriber don't know each other |
| Mediator | No middle layer | Message broker / event bus in between |
| Synchrony | Usually synchronous | Often asynchronous |
| Scope | Single application | Can span multiple systems |
| Example | Java Swing, DOM events | Kafka, RabbitMQ, Redis Pub/Sub |
Note: Pub/Sub is a more decoupled evolution of the Observer pattern. Observer is the pattern; Pub/Sub is an architecture style based on the same idea but with a message broker separating publishers and subscribers.
React's useState and Vue's reactivity system are implementations of Observer — when state changes, all components subscribed to that state automatically re-render.
Java's standard library has built-in Observable class and Observer interface. Android's LiveData and RxJava are built on the same concept.
Django's signal system (post_save, pre_delete) is a textbook Observer implementation — models broadcast signals; any registered receiver reacts to them.
Redux's store.subscribe() directly uses the Observer pattern. Every connected component is an observer of the global store. Zustand follows the same model with a simpler API.
element.addEventListener('click', handler) — you're subscribing an observer (handler) to a subject (DOM element). Multiple handlers can observe the same event independently.