19. Observer
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. It is the foundation of event-driven programming, reactive systems, and MVC architectures.
Intent and Motivation
Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Motivation: A stock price changes and multiple displays (mobile app, web dashboard, email alert) must update. Hard-wiring each display into the Stock class creates tight coupling and makes adding new displays painful. Observer separates Subjects (publishers) from Observers (subscribers). The subject maintains a list of observers and calls update() on each when state changes. Observers register and unregister dynamically. The subject knows nothing about what observers do with the notification.
This is the most widely used behavioral pattern — events, listeners, signals, and reactive streams are all Observer variants.
Structure (UML-like)
┌──────────────┐ attach/detach ┌──────────────┐
│ Observer │ ◄────────────── │ Subject │
├──────────────┤ ├──────────────┤
│ + update() │ notify ──────► │ - observers[]│
└──────▲───────┘ │ + attach() │
│ │ + detach() │
│ implements │ + notify() │
┌──────┴───────┐ └──────────────┘
│ConcreteObs. A│
│ConcreteObs. B│
└──────────────┘
Participants:
- Subject — knows its observers; sends notifications when state changes.
- Observer — defines an updating interface for objects that should be notified.
- ConcreteSubject — stores state; sends notification when state changes.
- ConcreteObserver — stores a reference to ConcreteSubject; implements update logic.
Java Example
import java.util.*;
// Observer
interface StockObserver {
void update(String symbol, double price);
}
// Subject
class StockMarket {
private final Map<String, Double> prices = new HashMap<>();
private final List<StockObserver> observers = new ArrayList<>();
void addObserver(StockObserver o) { observers.add(o); }
void removeObserver(StockObserver o) { observers.remove(o); }
void setPrice(String symbol, double price) {
prices.put(symbol, price);
notifyObservers(symbol, price);
}
private void notifyObservers(String symbol, double price) {
for (StockObserver o : observers) {
o.update(symbol, price);
}
}
}
// Concrete Observers
class MobileApp implements StockObserver {
public void update(String symbol, double price) {
System.out.println("Mobile: " + symbol + " = $" + price);
}
}
class EmailAlert implements StockObserver {
private final double threshold;
EmailAlert(double threshold) { this.threshold = threshold; }
public void update(String symbol, double price) {
if (price > threshold) {
System.out.println("Email alert: " + symbol + " exceeded $" + threshold);
}
}
}
// Usage
StockMarket market = new StockMarket();
market.addObserver(new MobileApp());
market.addObserver(new EmailAlert(150.0));
market.setPrice("AAPL", 155.0);
JavaScript Example
class EventEmitter {
constructor() {
this.listeners = {};
}
on(event, callback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
return () => this.off(event, callback); // unsubscribe function
}
off(event, callback) {
this.listeners[event] = (this.listeners[event] || [])
.filter(cb => cb !== callback);
}
emit(event, data) {
(this.listeners[event] || []).forEach(cb => cb(data));
}
}
const store = new EventEmitter();
const unsub = store.on('user:login', (user) => {
console.log('Dashboard updated for', user.name);
});
store.on('user:login', (user) => {
console.log('Analytics tracked login:', user.id);
});
store.emit('user:login', { id: 1, name: 'Alice' });
unsub(); // remove first listener
Real-World Use Cases
| Framework / System | Usage |
|---|---|
Java Observable / listeners |
Swing/AWT event listeners; property change listeners. |
| JavaScript DOM events | addEventListener / dispatchEvent — native Observer. |
React useState / useEffect |
State changes notify components to re-render. |
| RxJS Observables | Reactive streams extend Observer with operators and composition. |
Spring ApplicationEvent |
publishEvent() notifies all registered ApplicationListeners. |
| Kafka / RabbitMQ | Distributed observer — producers notify consumer groups. |
Pros and Cons
| Pros | Cons |
|---|---|
| Open/Closed — add new observers without changing the subject | Unexpected updates if observer chain triggers cascading notifications |
| Supports broadcast communication loosely coupled | Memory leaks if observers are not unsubscribed |
| Runtime subscription/unsubscription | Order of notification is often undefined |
| Foundation for reactive and event-driven architectures | Debugging event flows in large systems is difficult |
| Subject and observers can vary independently | “Notification overload” — too many events degrade performance |
When to Use vs When NOT to Use
Use when:
- A change to one object requires changing others, and you do not know how many.
- An object must notify other objects without knowing who they are.
- Abstractions have two aspects, one dependent on the other (model/view).
- You are building event-driven, reactive, or pub-sub systems.
Do NOT use when:
- Communication is one-to-one and simple (direct call is clearer).
- Observer chains create unpredictable cascading updates (consider Mediator).
- Performance requires guaranteed notification order (Observer is typically unordered).
- Observers are long-lived and rarely unsubscribed (memory leak risk).
Common Mistakes
- Memory leaks — observers not removed when components are destroyed.
- Cascading updates — observer A’s update triggers subject change, notifying observer B, which triggers A again (infinite loop).
- Fat notification payloads — sending entire state instead of delta changes wastes bandwidth.
- Synchronous notification blocking — slow observers delay all others; consider async notification.
- Tight coupling via concrete subject type — observers should depend on an interface, not a concrete class.
Related Patterns
- Mediator — encapsulates core coordination; Observer distributes notification without a central coordinator.
- Command — observer notifications can trigger command execution.
- Memento — used with Observer in MVC to store view state before model changes.
- Chain of Responsibility — event bubbling is a form of observer chain along the DOM tree.
- Publish-Subscribe — architectural extension of Observer with message brokers for distributed systems.