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

The object being observed is called the Subject, while the dependent objects are called Observers. Observers register themselves with the subject and receive notifications whenever the subject's state changes.

This pattern promotes loose coupling because the subject does not need to know the concrete implementation of its observers. It simply notifies all registered observers through a common interface.

The Observer Pattern is widely used in event-driven systems, GUI frameworks, messaging systems, monitoring applications, and publish-subscribe architectures.

Structure

The Observer Pattern consists of the following participants.

1. The Subject maintains a list of observers and notifies them whenever its state changes.
2. The Observer defines the interface that all observers must implement.
3. Concrete Observers receive notifications and perform the required action.

Java Implementation

Suppose an e-commerce application needs to notify multiple services whenever a new order is placed.

The inventory service updates stock, the email service sends a confirmation email, and the analytics service records the event.

Create the observer interface.
public interface OrderObserver {
    void update(String orderId);
}
Create the subject.
import java.util.ArrayList;
import java.util.List;

public class OrderService {
    private final List<OrderObserver> observers = new ArrayList<>();

    public void addObserver(OrderObserver observer) {
        observers.add(observer);
    }

    public void removeObserver(OrderObserver observer) {
        observers.remove(observer);
    }

    public void placeOrder(String orderId) {
        System.out.println("Order placed: " + orderId);

        for (OrderObserver observer : observers) {
            observer.update(orderId);
        }
    }
}
Create the inventory observer.
public class InventoryService implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("Inventory updated for " + orderId);
    }
}
Create the email observer.
public class EmailService implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("Confirmation email sent for " + orderId);
    }
}
Create the analytics observer.
public class AnalyticsService implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("Analytics recorded for " + orderId);
    }
}
The client registers observers and places an order.
public class Main {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();

        orderService.addObserver(new InventoryService());
        orderService.addObserver(new EmailService());
        orderService.addObserver(new AnalyticsService());

        orderService.placeOrder("ORD-1001");
    }
}
Output:
Order placed: ORD-1001
Inventory updated for ORD-1001
Confirmation email sent for ORD-1001
Analytics recorded for ORD-1001

How It Works?

The client first creates the OrderService object, which acts as the subject. It then registers multiple observers using the addObserver() method.

Whenever a new order is placed, the subject iterates through its list of registered observers and invokes the update() method on each one.

Each observer performs its own independent task without knowing about the existence or implementation of the other observers.

Similarly, the subject only depends on the OrderObserver interface and remains unaware of the concrete observer classes.

This design makes it easy to add new observers, such as SMS notifications, billing services, or audit logging, without modifying the subject or existing observers.

Advantages

1. It promotes loose coupling between the subject and its observers.
2. New observers can be added without modifying the subject.
3. Multiple objects can react automatically to the same event.
4. It simplifies the implementation of event-driven applications.

Disadvantages

1. A large number of observers can increase notification overhead.
2. Debugging may become difficult because multiple observers respond to the same event.
3. If observers are not removed properly, unnecessary references may lead to memory leaks.

JDK Examples

The Observer Pattern appears throughout the Java ecosystem.

1. The java.beans.PropertyChangeSupport class allows objects to notify registered PropertyChangeListener instances whenever a property changes.

2. The java.util.Observer and java.util.Observable classes implemented the Observer Pattern but were deprecated in Java 9 in favour of more flexible event-handling mechanisms.

Summary

The Observer Pattern establishes a one-to-many relationship between objects so that changes in one object automatically notify all registered observers.

By decoupling event producers from event consumers, the pattern provides a flexible and extensible mechanism for implementing event-driven systems, notifications, and publish-subscribe style communication.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer • Java • Python • Distributed Systems • AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

💬 Comments

Join the Discussion