Java Programs›OOP›Implement the Observer Pattern

Implement the Observer Pattern in Java

advanced·  OOP  ·  Design Patterns

Problem

The Observer pattern lets one object, the subject, notify any number of observer objects when its state changes, without knowing what those observers actually do.

Model a product whose price changes, with an email alert that reacts only to price drops and a log that records every change; observers can subscribe and unsubscribe at any time.

Input
price 120 → 99 → 105 → (unsubscribe email) → 89
Output
email fires once; the log records all three changes

Java Program

Java
import java.util.ArrayList; import java.util.List; public class ObserverPattern { interface PriceObserver { void onPriceChange(String product, double oldPrice, double newPrice); } static class Product { // the subject being watched private final String name; private double price; private final List<PriceObserver> observers = new ArrayList<>(); Product(String name, double price) { this.name = name; this.price = price; } void subscribe(PriceObserver o) { observers.add(o); } void unsubscribe(PriceObserver o) { observers.remove(o); } void setPrice(double newPrice) { double old = price; price = newPrice; for (PriceObserver o : observers) { o.onPriceChange(name, old, newPrice); // notify everyone who subscribed } } } static class EmailAlert implements PriceObserver { public void onPriceChange(String product, double oldPrice, double newPrice) { if (newPrice < oldPrice) System.out.println("Email: " + product + " dropped to " + newPrice); } } static class PriceLog implements PriceObserver { public void onPriceChange(String product, double oldPrice, double newPrice) { System.out.println("Log: " + product + " " + oldPrice + " -> " + newPrice); } } public static void main(String[] args) { Product headphones = new Product("Headphones", 120.0); PriceObserver email = new EmailAlert(); headphones.subscribe(email); headphones.subscribe(new PriceLog()); headphones.setPrice(99.0); // both observers react headphones.setPrice(105.0); // only the log reacts to a rise headphones.unsubscribe(email); headphones.setPrice(89.0); // email no longer subscribed } }

Output

Email: Headphones dropped to 99.0 Log: Headphones 120.0 -> 99.0 Log: Headphones 99.0 -> 105.0 Log: Headphones 105.0 -> 89.0

Core Logic

The subject keeps a list of observers behind a small interface and calls each one whenever its state changes, so new kinds of observer can be added without touching the subject.

How It Works
  1. 1PriceObserver is the interface every observer implements.
  2. 2Product stores its observers and offers subscribe and unsubscribe.
  3. 3setPrice updates the price, then calls onPriceChange on every subscriber.
  4. 4EmailAlert reacts only to drops, while PriceLog records every change.
After the email alert unsubscribes, the drop to 89 is logged but no email line is printed.
💡

Key Point: The subject depends only on the interface, never on concrete observer classes — that loose coupling is the whole point of the pattern.

Key Concepts

interfaceloose couplingsubscribe and unsubscribedesign pattern

Approach 2: Using Lambdas as Listeners

Java
import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; public class ObserverWithLambdas { static class EventBus<T> { private final List<Consumer<T>> listeners = new ArrayList<>(); void on(Consumer<T> listener) { listeners.add(listener); } void publish(T event) { listeners.forEach(l -> l.accept(event)); } } public static void main(String[] args) { EventBus<String> orders = new EventBus<>(); List<String> audit = new ArrayList<>(); orders.on(id -> System.out.println("Ship order " + id)); orders.on(id -> System.out.println("Email receipt for " + id)); orders.on(audit::add); // a method reference works as a listener too orders.publish("A-101"); orders.publish("A-102"); System.out.println("Audit trail: " + audit); } }

Output

Ship order A-101 Email receipt for A-101 Ship order A-102 Email receipt for A-102 Audit trail: [A-101, A-102]

Core Logic

Since Java 8, a functional interface such as Consumer&lt;T&gt; can replace a custom observer interface, so listeners become one-line lambdas.

How It Works
  1. 1EventBus&lt;T&gt; keeps a list of Consumer&lt;T&gt; listeners.
  2. 2on registers a listener; publish calls each one with the event.
  3. 3Two lambdas print messages, and audit::add stores every event in a list.
Publishing A-101 triggers the ship and email listeners and adds the ID to the audit trail.
💡

Key Point: Listeners are called in registration order on the publishing thread — a slow listener delays all the others, which is why larger systems hand events to a queue or executor.

Key Concepts

Consumer<T>lambda listenermethod referencegeneric event bus

Interview Questions

What problem does the Observer pattern solve?

It lets one object announce changes to many others without depending on their concrete classes, so new reactions can be added without editing the subject.

What can go wrong if observers are never unsubscribed?

The subject keeps references to them, so they can't be garbage-collected — a common memory leak in long-running applications with listeners.

Learn the Concept

Related Programs