The Observer pattern establishes a one-to-many dependency between objects. When the state of a single "subject" (the observable) changes, all registered "observers" are notified and updated automatically. This decoupling allows the subject to broadcast changes without needing to know the specific implementation details of its listeners.
Core Components
In a standard implemantation, the Subject maintains a colection of observers and provides methods to attach or detach them. The Observer defines a interface with an update method that the subject calls to synchronize state.
While modern Java development often favors reactive streams or custom listener interfaces, the JDK historically provided built-in support through the java.util.Observable class and the java.util.Observer interface. The following example demonstrates a stock market monitoring system using this architecture.
The Observable Subject: MarketDataProvider
The MarketDataProvider class tracks specific stock metrics. It triggers a notification whenever the price or volume data is refreshed.
import java.util.Observable;
public class MarketDataProvider extends Observable {
private String ticker;
private double currentPrice;
private double dailyHigh;
private double dailyLow;
private long tradeVolume;
public MarketDataProvider() {}
public void publishUpdate(String ticker, double price, double high, double low, long volume) {
this.ticker = ticker;
this.currentPrice = price;
this.dailyHigh = high;
this.dailyLow = low;
this.tradeVolume = volume;
// Mark the state as changed and trigger notifications
setChanged();
notifyObservers();
}
public String getTicker() { return ticker; }
public double getCurrentPrice() { return currentPrice; }
public double getDailyHigh() { return dailyHigh; }
public double getDailyLow() { return dailyLow; }
public long getTradeVolume() { return tradeVolume; }
}
The Institutional Observer: AnalyticsEngine
This observer represents a complex system that monitors all available data points for deep analysis. It registers itself with the provider during instantiation.
import java.util.Observable;
import java.util.Observer;
import java.text.NumberFormat;
public class AnalyticsEngine implements Observer {
private String ticker;
private double price;
private long volume;
public AnalyticsEngine(Observable provider) {
provider.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof MarketDataProvider) {
MarketDataProvider data = (MarketDataProvider) o;
this.ticker = data.getTicker();
this.price = data.getCurrentPrice();
this.volume = data.getTradeVolume();
renderAnalytics();
}
}
private void renderAnalytics() {
NumberFormat nf = NumberFormat.getCurrencyInstance();
System.out.println("[Institutional Analytics] Processing data for: " + ticker);
System.out.println("\tPrice: " + nf.format(price));
System.out.println("\tVolume: " + volume + " units");
System.out.println();
}
}
The Retail Observer: MobileAlertSystem
A simpler observer might only care about a subset of the data, such as the current price ticker.
import java.util.Observable;
import java.util.Observer;
public class MobileAlertSystem implements Observer {
private String ticker;
private double price;
public MobileAlertSystem(Observable provider) {
provider.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof MarketDataProvider) {
MarketDataProvider data = (MarketDataProvider) o;
this.ticker = data.getTicker();
this.price = data.getCurrentPrice();
sendPushNotification();
}
}
private void sendPushNotification() {
System.out.println("[Mobile Alert] " + ticker + " is trading at $" + price);
}
}
Execution and Test Logic
The driver class initializes the subject and attaches multiple observers. When the data provider updates its state, all connected systems react according to their internal logic.
public class StockMarketSimulator {
public static void main(String[] args) {
MarketDataProvider provider = new MarketDataProvider();
// Initialize observers and link them to the subject
new AnalyticsEngine(provider);
new MobileAlertSystem(provider);
System.out.println("--- Starting Market Feed ---\n");
// Simulating incoming data streams
provider.publishUpdate("AAPL", 150.25, 155.00, 148.50, 5000000L);
provider.publishUpdate("GOOGL", 2800.10, 2810.00, 2790.50, 1200000L);
provider.publishUpdate("TSLA", 720.50, 730.00, 710.20, 3500000L);
}
}