Master the Observer Pattern in Java: Theory, Code, and Real‑World Example
This article explains the Observer pattern, compares it with the Publish‑Subscribe model, showcases Java's built‑in Observable and Observer classes, provides complete code examples, and discusses practical considerations such as memory leaks and performance optimizations.
Hello everyone, are you still coding with plain for loops? Who hasn't used the Observer pattern?
This article presents the theory and practice of the Observer pattern .
What Is the Observer Pattern?
The Observer pattern defines a one‑to‑many dependency between objects so that when one object (the subject) changes state, all its dependents (observers) are automatically notified and updated.
The changing object is called the observable , and the objects that receive notifications are called observers . An observable can have multiple observers, typically stored in a list that can be dynamically modified, making the pattern easy to extend.
Advantages include loose coupling between the observable and its observers, reducing overall system coupling.
Publish‑Subscribe Model
The Observer pattern is often referred to as the Publish‑Subscribe model, though there are subtle differences.
In the Observer pattern, observers are directly bound to the observable, which maintains a list of observers; the relationship is based on interfaces, so while loosely coupled, it is not completely decoupled.
In the Publish‑Subscribe model, publishers and subscribers have no direct relationship. A middle‑man (topic broker) distributes messages, making the system fully decoupled.
Observer Pattern in the JDK
The JDK provides built‑in support for the Observer pattern since version 1.0, so you don't need to reinvent the wheel.
Observable class:
java.util.Observable
Key fields:
changed : indicates whether the observable's state has changed (default false).
obs : a thread‑safe list of observers (Vector), initially empty.
Observer interface:
java.util.Observer
<code>public interface Observer {<br/> /**<br/> * This method is called whenever the observed object is changed.<br/> * An application calls an Observable object's notifyObservers method<br/> * to have all the object's observers notified of the change.<br/> *<br/> * @param o the observable object.<br/> * @param arg an argument passed to the notifyObservers method.<br/> */<br/> void update(Observable o, Object arg);<br/>}</code>The Observer interface contains a single
updatemethod used to notify observers of changes.
Practical Example
We will implement a simple article‑push scenario: the observable is the author ("JavaStack"), and the observers are readers who receive updates when a new article is published.
Observable implementation:
<code>import lombok.Getter;<br/><br/>import java.util.Observable;<br/><br/>/**<br/> * Observable: the author<br/> */<br/>@Getter<br/>public class JavaStackObservable extends Observable {<br/><br/> private String article;<br/><br/> /**<br/> * Publish an article<br/> * @param article the article content<br/> */<br/> public void publish(String article) {<br/> // set article<br/> this.article = article;<br/><br/> // change state<br/> this.setChanged();<br/><br/> // notify all observers<br/> this.notifyObservers();<br/> }<br/>}<br/></code>The observable publishes an article, marks itself as changed, and notifies all observers.
Key method
notifyObservers(from JDK) acquires a lock, checks the changed flag, clears it, and iterates over the observer list to invoke each observer's
updatemethod.
Observer implementation:
<code>import lombok.NonNull;<br/>import lombok.RequiredArgsConstructor;<br/><br/>import java.util.Observable;<br/>import java.util.Observer;<br/><br/>/**<br/> * Observer: a reader fan<br/> */<br/>@RequiredArgsConstructor<br/>public class ReaderObserver implements Observer {<br/><br/> @NonNull<br/> private String name;<br/><br/> private String article;<br/><br/> @Override<br/> public void update(Observable o, Object arg) {<br/> // update article<br/> updateArticle(o);<br/> }<br/><br/> private void updateArticle(Observable o) {<br/> JavaStackObservable javaStackObservable = (JavaStackObservable) o;<br/> this.article = javaStackObservable.getArticle();<br/> System.out.printf("I am reader %s, article updated to: %s\n", this.name, this.article);<br/> }<br/>}<br/></code>The observer casts the observable to
JavaStackObservable, retrieves the article, stores it, and prints a message.
Test class:
<code>public class ObserverTest {<br/><br/> public static void main(String[] args) {<br/> // create observable<br/> JavaStackObservable javaStackObservable = new JavaStackObservable();<br/><br/> // add observers<br/> javaStackObservable.addObserver(new ReaderObserver("Xiao Ming"));<br/> javaStackObservable.addObserver(new ReaderObserver("Xiao Zhang"));<br/> javaStackObservable.addObserver(new ReaderObserver("Xiao Ai"));<br/><br/> // publish article<br/> javaStackObservable.publish("What is the Observer pattern?");<br/> }<br/><br/>}<br/></code>Observers must be added before publishing; otherwise they won't receive updates.
Running the program produces output similar to:
Conclusion
Through this simple article‑push example, you should now have a solid understanding of the Observer pattern and can apply it to any one‑to‑many dependency scenario.
While the pattern decouples subjects and observers, it can lead to memory leaks if many observers are retained, and all observer updates run sequentially in a single loop, which may become a performance bottleneck; consider asynchronous updates or thread pools for better efficiency.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.