Observer Pattern in Java

Naveen AnamaneniUpdated · 5 min read

When an online order ships, several things should happen: the customer gets an SMS, the email system sends tracking details, and the analytics dashboard updates. The Order class shouldn't have to know about SMS, email and analytics. The Observer pattern lets interested objects subscribe to an object, the subject, and the subject simply notifies every subscriber when something changes. Subscribers can come and go without the subject changing at all.

Order-Status Notifications

The subject notifies whoever has subscribed
Tracking.java▶ Try it
import java.util.ArrayList;
import java.util.List;
 
public class Tracking {
    interface StatusListener {
        void onStatusChange(String orderId, String status);
    }
 
    static class Order {
        private final String id;
        private final List<StatusListener> listeners = new ArrayList<>();
 
        Order(String id) { this.id = id; }
 
        void subscribe(StatusListener l) { listeners.add(l); }
        void unsubscribe(StatusListener l) { listeners.remove(l); }
 
        void setStatus(String status) {
            for (StatusListener l : listeners) {
                l.onStatusChange(id, status);
            }
        }
    }
 
    static class SmsAlert implements StatusListener {
        public void onStatusChange(String id, String status) {
            System.out.println("SMS to Navika: order " + id + " is " + status);
        }
    }
 
    static class EmailAlert implements StatusListener {
        public void onStatusChange(String id, String status) {
            System.out.println(
                "Email to Navika: order " + id + " is " + status);
        }
    }
 
    static class Dashboard implements StatusListener {
        int updates = 0;
        public void onStatusChange(String id, String status) {
            updates++;
        }
    }
 
    public static void main(String[] args) {
        Order order = new Order("ORD-301");
        StatusListener email = new EmailAlert();
        Dashboard dashboard = new Dashboard();
        order.subscribe(new SmsAlert());
        order.subscribe(email);
        order.subscribe(dashboard);
 
        order.setStatus("SHIPPED");
        order.unsubscribe(email);
        order.setStatus("DELIVERED");
        System.out.println("Dashboard updates: " + dashboard.updates);
    }
}
Output
SMS to Navika: order ORD-301 is SHIPPED
Email to Navika: order ORD-301 is SHIPPED
SMS to Navika: order ORD-301 is DELIVERED
Dashboard updates: 2

How it works

  1. StatusListener is the observer interface. Anything that wants updates implements it.
  2. Order is the subject. It keeps a list of listeners and offers subscribe and unsubscribe.
  3. setStatus notifies every listener in the list. Order knows only the interface, never SmsAlert or Dashboard.
  4. After the email alert unsubscribes, the DELIVERED change reaches only the SMS alert and the dashboard, which counted both updates.

Where You'll Meet It

PlaceSubjectObservers
Swing and JavaFXButtons, text fieldsAction and change listeners
java.beans.PropertyChangeSupportAny beanPropertyChangeListeners
Event systems and message queuesEvent publishersEvent handlers, consumers
Reactive streams (java.util.concurrent.Flow)PublishersSubscribers
Spring frameworkApplicationEventPublisher@EventListener methods

The old java.util.Observer and java.util.Observable classes are deprecated since Java 9 (the compiler warns about them). Write your own small listener interface, as above, or use PropertyChangeSupport or Flow.

Common Mistakes

Slow or failing observers. One observer that throws or blocks stops the others; keep them fast, and catch exceptions around each call if needed.

Modifying the list while notifying. An observer that unsubscribes itself during the loop causes a ConcurrentModificationException; iterate over a copy or use CopyOnWriteArrayList (covered later, with concurrency).

Relying on notification order. Observers shouldn't depend on being called first or last.

Interview Corner

What is the observer pattern?

The observer pattern defines a one-to-many dependency: a subject maintains a list of observers that implement a common interface and notifies all of them when its state changes. Observers can subscribe and unsubscribe at runtime, and the subject depends only on the observer interface, which keeps them loosely coupled. GUI listeners and event systems are typical examples.

Why is java.util.Observable deprecated?

It's a class rather than an interface, so subjects must extend it and can't extend anything else; its notification model is limited and not thread-safe in a useful way, and the order of notifications is unspecified. Custom listener interfaces, PropertyChangeSupport or the reactive Flow API are recommended instead.

Practice

  1. Easy: Add an AppPushAlert observer that prints a push notification for Advitha, and subscribe it to the order.
  2. Harder: Write a StockPrice subject that notifies observers only when the price changes by more than 2%, with a PriceAlert observer that prints the old and new price.

Quick check: Observer

4 questions
1What does the subject know about its observers?
2After an observer unsubscribes, it:
3What memory problem can observers cause?
4What's the status of java.util.Observable?

Key Takeaways

Was this tutorial helpful?

Lesson 186 of 482