The Observer Pattern is a behavioral design pattern in software engineering. It defines a one-to-many relationship between objects: when the state of one object (the subject) changes, all its dependents (the observers) are automatically notified. This pattern enables components to remain loosely coupled. The subject does not need to know the details of its observers, and observers can be added or removed dynamically.
Interfaces
At the core of the Observer Pattern are three interfaces (or roles).

The Subject maintains a list of observers and notifies them whenever its state changes.
public interface Subject<T> {
void attach(Observer<T> observer);
void detach(Observer<T> observer);
}The Observer reacts to updates from the subject. Each observer implements a single method, typically called update, that receives a Change.
@FunctionalInterface
public interface Observer<T> {
void update(Change<T> change);
}The Change encapsulates information about what has changed, allowing observers to respond appropriately.
public interface Change<T> {
T oldValue();
T newValue();
}This separation keeps the system loosely coupled: the subject does not depend on any specific observer, only on the Observer interface.
Abstract Classes
For better code reuse, consider defining an abstract class to capture the common logic shared by every Subject.

public abstract class AbstractSubject<T> implements Subject<T> {
private final List<Observer<T>> observers;
protected AbstractSubject() {
this.observers = new ArrayList<>();
}
public final void attach(Observer<T> observer) {
observers.add(observer);
}
public final void detach(Observer<T> observer) {
observers.remove(observer);
}
protected final void notifyObservers(Change<T> change) {
observers.forEach(observer -> observer.update(change));
}
}With that in mind, let’s see the Observer Pattern in practice.
Case Study: Real-Time Stock Prices
To see how the Observer Pattern works in practice, let’s model a real-time stock price system. Each Stock acts as a Subject that notifies multiple Observers whenever its price changes.
First, let’s expand the previous UML class diagram to include concrete classes:
- Stock (Subject): has a symbol and current price
- StockPriceLogger (Observer): logs all stock prices to PrintStream
- StockPriceAlerter (Observer): logs when price goes below threshold to PrintStream
- StockPriceChange (Change): the stock price change event
- StockExchangeSimulator: the application entry point (main)

Subjects
Our Stock (Subject) notifies its registered observers when its price changes.
public class Stock extends AbstractSubject<Double> {
private final String symbol;
private Double price;
public Stock(String symbol, Double initialPrice) {
this.symbol = symbol;
this.price = initialPrice;
}
public String getSymbol() {
return symbol;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
double oldPrice = this.price;
double newPrice = price;
this.price = newPrice;
notifyObservers(new PriceChange(oldPrice, newPrice));
}
}Observers
The StockPriceLogger logs the old and new value to PrintStream.
public final class StockPriceLogger implements Observer<Double> {
private final PrintStream out;
public StockPriceLogger(PrintStream out) {
this.out = out;
}
@Override
public void update(Change<Double> change) {
out.printf("Price changed: %4.2fs -> %4.2f\n",
change.oldValue(),
change.newValue());
}
}The StockPriceAlerter logs the new value to PrintStream if it’s below its threshold.
public final class StockPriceAlerter implements Observer<Double> {
private final PrintStream out;
private final Double threshold;
public StockPriceAlerter(PrintStream out, Double threshold) {
this.out = out;
this.threshold = threshold;
}
@Override
public void update(Change<Double> change) {
if (change.newValue() >= threshold) {
return;
}
out.printf("[Alert] Price below threshold: %s\n", change.newValue());
}
}Changes
The PriceChange is just a record with the old and new Double value.
public record PriceChange(Double oldValue, Double newValue)
implements Change<Double> {}
Main
And finally, our main wires it all together:
- Creates a list of stock subjects
- Creates the alerter and logger observers
- Attaches the observers to each stock
- Infinite loop that simulates stock price changes and updates the subjects
public class StockExchangeSimulator {
public static void main(String[] args) throws InterruptedException {
Random random = new Random();
// Add stocks
List<Stock> stocks = new ArrayList<>();
stocks.add(new Stock("AAPL", 100.00));
// Set up observers
StockPriceAlerter alerter = new StockPriceAlerter(System.out, 98.00);
StockPriceLogger logger = new StockPriceLogger(System.out);
stocks.forEach(stock -> stock.attach(alerter));
stocks.forEach(stock -> stock.attach(logger));
// Run app
while (true) {
Thread.sleep(1000);
stocks.forEach(stock -> {
double newPrice = stock.getPrice() + random.nextDouble(1) - 0.67;
stock.setPrice(newPrice);
});
}
}
}Java’s Built-In Observer Pattern Implementation
Instead of rolling out your own interfaces, you could also use Java’s built-in Observer Pattern implementation from javax.beans.*
Mapping:
- Subject → PropertyChangeSupport
- Observer → PropertyChangeListener
- Change → PropertyChangeEvent
This is how the previous Real-Time Stock Price application would look like (changes in blue):

javax.beans.PropertyChange*Whenever you use any information, text, code, or images from this guide, always reference back to it as the source: www.Sorn.dev



