Observer Pattern
BeginnerDefines a one-to-many dependency so that when one object (Subject) changes state, all its dependents (Observers) are notified automatically.
Overview
The Observer pattern implements a publish-subscribe mechanism. The Subject maintains a list of Observer references and calls update() on each when its state changes. In Java, this is embodied by java.util.Observable (deprecated in Java 9 — use EventListener pattern or Spring's ApplicationEvent instead), PropertyChangeListener, and Swing event listeners. Reactive frameworks (RxJava, Project Reactor) extend Observer to support backpressure and error handling. Key trade-off: loose coupling between Subject and Observer vs potential memory leaks (Observer not removed when no longer needed — "lapsed listener" problem).
Observer Implementation
Observer interface has an update() method. Subject maintains a list of Observers and provides register/deregister methods. ConcreteSubject stores state and notifies all observers on change.
import java.util.ArrayList;
import java.util.List;
// Observer interface
public interface StockObserver {
void onPriceChange(String symbol, double newPrice, double oldPrice);
}
// Subject interface
public interface StockSubject {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
// Concrete Subject
public class StockTicker implements StockSubject {
private final String symbol;
private double currentPrice;
private final List<StockObserver> observers = new ArrayList<>();
public StockTicker(String symbol, double initialPrice) {
this.symbol = symbol;
this.currentPrice = initialPrice;
}
@Override public void addObserver(StockObserver o) { observers.add(o); }
@Override public void removeObserver(StockObserver o) { observers.remove(o); }
public void setPrice(double newPrice) {
double oldPrice = this.currentPrice;
this.currentPrice = newPrice;
notifyObservers(oldPrice); // state changed — notify
}
private void notifyObservers(double oldPrice) {
observers.forEach(o -> o.onPriceChange(symbol, currentPrice, oldPrice));
}
@Override public void notifyObservers() {
observers.forEach(o -> o.onPriceChange(symbol, currentPrice, currentPrice));
}
}
// Concrete Observers
public class AlertObserver implements StockObserver {
private final double threshold;
public AlertObserver(double threshold) { this.threshold = threshold; }
@Override
public void onPriceChange(String symbol, double newPrice, double oldPrice) {
if (Math.abs(newPrice - oldPrice) / oldPrice > threshold) {
System.out.printf("ALERT: %s moved by >%.0f%% → ₹%.2f%n",
symbol, threshold * 100, newPrice);
}
}
}
public class PortfolioObserver implements StockObserver {
private final int quantity;
public PortfolioObserver(int quantity) { this.quantity = quantity; }
@Override
public void onPriceChange(String symbol, double newPrice, double oldPrice) {
double pnl = (newPrice - oldPrice) * quantity;
System.out.printf("Portfolio P&L for %s: %+.2f%n", symbol, pnl);
}
}
// Usage
StockTicker infosys = new StockTicker("INFY", 1500.0);
infosys.addObserver(new AlertObserver(0.05)); // alert on 5% move
infosys.addObserver(new PortfolioObserver(100)); // holds 100 shares
infosys.setPrice(1600.0); // triggers both observers
infosys.setPrice(1580.0); // triggers both observersSpring ApplicationEvent (Production Observer)
Spring's event system is the production-grade Observer. Publish events with ApplicationEventPublisher; handle with @EventListener. Events are synchronous by default; use @Async for background processing.
// Event (value object)
public record OrderPlacedEvent(String orderId, String userId, double amount) {}
// Publisher (Subject)
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Transactional
public Order placeOrder(OrderRequest request) {
Order order = orderRepository.save(Order.from(request));
eventPublisher.publishEvent(new OrderPlacedEvent(
order.getId(), order.getUserId(), order.getTotal()));
return order;
}
}
// Observers — decoupled listeners
@Component
public class EmailNotificationListener {
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
emailService.sendConfirmation(event.userId(), event.orderId());
}
}
@Component
public class AnalyticsListener {
@Async // runs in a separate thread
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
analyticsService.trackOrder(event.orderId(), event.amount());
}
}Key Points to Remember
- 1Observer defines one-to-many dependency — Subject notifies all registered observers on state change.
- 2The "lapsed listener" problem: failing to deregister observers causes memory leaks.
- 3Spring's @EventListener is the production Observer implementation — use it over raw Observer.
- 4java.util.Observable is deprecated since Java 9 — avoid it.
- 5@Async on @EventListener makes the observer run on a separate thread (fire-and-forget).
- 6Reactive streams (RxJava, Project Reactor) extend Observer with backpressure, error handling, and operators.
Interview Questions
Sign in to ask AriaWhat is the lapsed listener problem in Observer pattern and how do you fix it?
What is the difference between Observer and Pub-Sub patterns?
How does Spring's ApplicationEvent use the Observer pattern?
How would you make Observer thread-safe?
What is the difference between push and pull models in Observer?
Ask Aria about Observer Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.