Implement the Observer Pattern in Java
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.
Java Program
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
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.
- 1
PriceObserveris the interface every observer implements. - 2
Productstores its observers and offerssubscribeandunsubscribe. - 3
setPriceupdates the price, then callsonPriceChangeon every subscriber. - 4
EmailAlertreacts only to drops, whilePriceLogrecords every change.
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
Approach 2: Using Lambdas as Listeners
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
Core Logic
Since Java 8, a functional interface such as Consumer<T> can replace a custom observer interface, so listeners become one-line lambdas.
- 1
EventBus<T>keeps a list ofConsumer<T>listeners. - 2
onregisters a listener;publishcalls each one with the event. - 3Two lambdas print messages, and
audit::addstores every event in a list.
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
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.