The Observer Pattern establishes a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. Think of it as a subscription model — interested parties register to receive updates, and the publisher broadcasts changes without knowing who is listening.
This pattern is the backbone of event-driven architectures. From GUI button clicks to microservice event buses, the Observer Pattern enables loose coupling between components that need to react to changes.
You are building an e-commerce platform. When an order is placed, several things must happen: send a confirmation email, update inventory, notify the analytics service, and trigger the shipping workflow. The naive approach is to put all this logic directly in the placeOrder() method.
This creates tight coupling — the order service now depends on the email service, inventory service, analytics service, and shipping service. Every time you add a new reaction to “order placed,” you must modify the order class. Testing becomes painful because you cannot place an order without triggering every downstream system.
The Observer Pattern decouples the event source (order service) from the event handlers (email, inventory, analytics). The order service maintains a list of observers and simply notifies them when an order is placed. Each observer decides independently how to react.
Adding a new reaction — say, a loyalty points service — requires only creating a new observer and registering it. The order service code remains untouched.
Strive for loosely coupled designs between objects that interact. The subject (publisher) knows nothing about its observers except that they implement a common interface. This means you can add, remove, or replace observers without modifying the subject.
Scenario: An order event system that notifies multiple services when an order is placed.
import java.util.ArrayList;
import java.util.List;
// Event data
public class OrderEvent {
private final String orderId;
private final String customerEmail;
private final double totalAmount;
private final List<String> items;
public OrderEvent(String orderId, String customerEmail,
double totalAmount, List<String> items) {
this.orderId = orderId;
this.customerEmail = customerEmail;
this.totalAmount = totalAmount;
this.items = items;
}
public String getOrderId() { return orderId; }
public String getCustomerEmail() { return customerEmail; }
public double getTotalAmount() { return totalAmount; }
public List<String> getItems() { return items; }
}
// Observer interface
public interface OrderObserver {
void onOrderPlaced(OrderEvent event);
}
// Subject (Publisher)
public class OrderService {
private final List<OrderObserver> observers = new ArrayList<>();
public void subscribe(OrderObserver observer) {
observers.add(observer);
}
public void unsubscribe(OrderObserver observer) {
observers.remove(observer);
}
public void placeOrder(String orderId, String email,
double total, List<String> items) {
System.out.println("Order " + orderId + " placed successfully.");
OrderEvent event = new OrderEvent(orderId, email, total, items);
// Notify all observers
for (OrderObserver observer : observers) {
observer.onOrderPlaced(event);
}
}
}
// Concrete Observer: Email Notification
public class EmailNotificationObserver implements OrderObserver {
@Override
public void onOrderPlaced(OrderEvent event) {
System.out.printf("[Email] Sending confirmation to %s for order %s ($%.2f)%n",
event.getCustomerEmail(), event.getOrderId(), event.getTotalAmount());
}
}
// Concrete Observer: Inventory Update
public class InventoryObserver implements OrderObserver {
@Override
public void onOrderPlaced(OrderEvent event) {
System.out.printf("[Inventory] Reserving %d items for order %s%n",
event.getItems().size(), event.getOrderId());
}
}
// Concrete Observer: Analytics Tracking
public class AnalyticsObserver implements OrderObserver {
@Override
public void onOrderPlaced(OrderEvent event) {
System.out.printf("[Analytics] Tracking order %s — revenue: $%.2f%n",
event.getOrderId(), event.getTotalAmount());
}
}
// Usage
public class Main {
public static void main(String[] args) {
OrderService orderService = new OrderService();
// Register observers
orderService.subscribe(new EmailNotificationObserver());
orderService.subscribe(new InventoryObserver());
orderService.subscribe(new AnalyticsObserver());
// Place an order — all observers get notified
orderService.placeOrder("ORD-1001", "jane@example.com", 149.99,
List.of("Laptop Stand", "USB-C Hub"));
}
}
Same order notification system in Python.
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class OrderEvent:
order_id: str
customer_email: str
total_amount: float
items: list[str]
# Observer interface
class OrderObserver(ABC):
@abstractmethod
def on_order_placed(self, event: OrderEvent) -> None:
pass
# Subject (Publisher)
class OrderService:
def __init__(self):
self._observers: list[OrderObserver] = []
def subscribe(self, observer: OrderObserver) -> None:
self._observers.append(observer)
def unsubscribe(self, observer: OrderObserver) -> None:
self._observers.remove(observer)
def place_order(self, order_id: str, email: str,
total: float, items: list[str]) -> None:
print(f"Order {order_id} placed successfully.")
event = OrderEvent(order_id, email, total, items)
# Notify all observers
for observer in self._observers:
observer.on_order_placed(event)
# Concrete Observer: Email Notification
class EmailNotificationObserver(OrderObserver):
def on_order_placed(self, event: OrderEvent) -> None:
print(f"[Email] Sending confirmation to {event.customer_email} "
f"for order {event.order_id} (${event.total_amount:.2f})")
# Concrete Observer: Inventory Update
class InventoryObserver(OrderObserver):
def on_order_placed(self, event: OrderEvent) -> None:
print(f"[Inventory] Reserving {len(event.items)} items "
f"for order {event.order_id}")
# Concrete Observer: Analytics Tracking
class AnalyticsObserver(OrderObserver):
def on_order_placed(self, event: OrderEvent) -> None:
print(f"[Analytics] Tracking order {event.order_id} "
f"— revenue: ${event.total_amount:.2f}")
# Usage
if __name__ == "__main__":
order_service = OrderService()
# Register observers
order_service.subscribe(EmailNotificationObserver())
order_service.subscribe(InventoryObserver())
order_service.subscribe(AnalyticsObserver())
# Place an order — all observers get notified
order_service.place_order(
"ORD-1001", "jane@example.com", 149.99,
["Laptop Stand", "USB-C Hub"]
)
ActionListener, MouseListener) to handle user interactions.ApplicationEvent — Spring’s event system lets beans publish and listen to application events using the observer pattern.addEventListener() is the observer pattern — elements are subjects, handlers are observers.