definition
Observer Pattern defines a one to many dependency. In this way, when an object changes state, all its dependencies will be notified and updated automatically.
——Head FIRST design mode
Observer mode is one of the most used modes in JDK.
AKA: publish subscribe mode (Publish/Subscribe)
Main role
- Observable / Subject: the abstract Subject role saves all references to observer objects in a collection (such as ArrayList and Vector). Each Subject can have any number of observers. Abstract topics provide an interface to add and delete observer objects.
- Create observable: store the relevant status into the specific observer object; Notify all registered observers when the internal state of a specific subject changes.
- Observer Observer: define an interface for all specific observers to update themselves when notified of the subject.
- Concreate Observer: stores the state corresponding to the observed. If necessary, the concrete observer role can maintain a reference to the concrete subject object.
The following figure (source: programmer small gray):
give an example
The weather station is realized from the Head FIRST design mode.
The weather station provides temperature, humidity, air pressure and other meteorological data. If the data is updated, it needs to track and obtain these data at any time and display them.
First, establish the interface
//Observer interface public interface Subject { void addObserver(Observer observer); void removerObserver(Observer observer); void notifyAllObservers(); }
public interface Observer { void update(float temp, float humidity); }
public interface DisplayElement { void display(); }
Implement observed WeatherData
public class WeatherData implements Subject{ private float temperature; private float humidity; private List<Observer> observerList; public WeatherData() { observerList = new ArrayList<Observer>(); } public void changeWeatherData(float temp,float humidity){ this.temperature = temp; this.humidity = humidity; notifyAllObservers(); } @Override public void addObserver(Observer observer) { observerList.add(observer); } @Override public void removerObserver(Observer observer) { int index = observerList.indexOf(observer); if(index >= 0) observerList.remove(index); } @Override public void notifyAllObservers() { for(Observer obs: observerList){ obs.update(temperature,humidity); } } }
Implement the weather bulletin board as an observer WeatherData
public class CurrentConditionsDisplay implements Observer,DisplayElement{ private float temperature; private float humidity; private String displayName; //The reference to Subject is retained here private Subject weatherData; public CurrentConditionsDisplay(String displayName, Subject weatherData) { this.displayName = displayName; this.weatherData = weatherData; weatherData.addObserver(this); } @Override public void update(float temp, float humidity) { this.temperature = temp; this.humidity = humidity; System.out.println(displayName + "Weather bulletin board: current temperature" + temperature + "℃,Humidity:" + humidity + "%"); } //Getter & setter ignore }
The reference to Subject is reserved here, which can be changed in practice.
The subject interface here can be changed to an abstract class and some general methods can be defined in the class. In fact, the built-in observer pattern in Java needs to inherit from an abstract class. The built-in observer mode in Java is described in detail below.
Observer mode under push-pull model
When the above code runs, when the weather changes, it will notify the observer of the change. That is, the topic object pushes the details of the topic to the observer, whether the observer needs it or not. This way is to push the model.
However, there are some needs. Observers want to actively pull down some data rather than passively feed data. Look at the code first
Example under pull model
For this example, if the logic is changed to pull mode, it can be modified as follows:
In WeatherData, the reference to the Subject is no longer retained, but passed in as a parameter
First, Observer modifies the interface parameters and directly passes in the Subject:
public interface Observer { //Note that the previous parameters are specific temperature and humidity values, and now they are Subject void update(Subject subject); }
When calling, you need to pass in an instance of Subject
public class WeatherData implements Subject{ private float temperature; private float humidity; private List<Observer> observerList; public WeatherData() { observerList = new ArrayList<Observer>(); } public void changeWeatherData(float temp,float humidity){ this.temperature = temp; this.humidity = humidity; notifyAllObservers(); } @Override public void addObserver(Observer observer) { observerList.add(observer); } @Override public void removerObserver(Observer observer) { int index = observerList.indexOf(observer); if(index >= 0) observerList.remove(index); } @Override public void notifyAllObservers() { for(Observer obs: observerList){ obs.update(this); } } }
Implement void update(Subject)
public class CurrentConditionsDisplay implements Observer,DisplayElement{ private float temperature; private float humidity; private String displayName; //The reference to Subject is retained here private Subject weatherData; public CurrentConditionsDisplay(String displayName, Subject weatherData) { this.displayName = displayName; this.weatherData = weatherData; weatherData.addObserver(this); } @Override public void update(Subject subject) { this.temperature = ((WeatherData)subject).temperature; this.humidity = ((WeatherData)subject).humidity; System.out.println(displayName + "Weather bulletin board: current temperature" + temperature + "℃,Humidity:" + humidity + "%"); } //Getter & setter ignore }
Comparison of push-pull models
Partial reference Java design pattern observer pattern (push-pull model)
- The push model assumes that the subject object knows the data needed by the observer; The pull model is that the subject object does not know what data the observer needs. When there is no way, it simply passes itself to the observer and lets the observer take values as needed.
- The push model may make it difficult to reuse the observer object, because the observer's update() method is a parameter defined as needed, and this method may no longer be applicable as the business changes in the future; The parameter of the pull model update() method is the subject object itself, which is basically the largest data set that the subject object can transfer. It can basically meet the needs of various situations, but the subject object needs to be completely exposed to the user.
Java built-in observer mode
Java itself has its own implementation Observer mode. In the java.util Library of the Java language, an Observable class and an Observer interface are provided.
Observer interface
JDK8 documentation Interface Observer
JDK9 deprecated Interface Observer
This interface only defines one method, update(). When the state of the observed object changes, the notifyObservers() method of the observed object will call this method.
package java.util; /** * A class can implement the <code>Observer</code> interface when it * wants to be informed of changes in observable objects. * When a class wants to be informed of changes in observable objects, it can implement the < code > observer < / code > interface * * @author Chris Warth * @see java.util.Observable * @since JDK1.0 */ public interface Observer { * * @param o the observable object Observed/Subject object * @param arg an argument passed to the <code>notifyObservers</code> method.Pass to notifyObservers Parameters of */ void update(Observable o, Object arg); }
Observable class
JDK8 documentation Class Observable
JDK9 deprecated Class Observable
The observed classes are subclasses of the java.util.Observable class.
There are two core functions:
- setChanged(): after being called, an internal tag variable will be set to represent that the state of the observed object has changed.
- notifyObservers(): when this method is called, it will call the update() method of all registered observer objects so that these observer objects can update themselves.
package java.util; /** * This class represents an observable object, or "data" * in the model-view paradigm. It can be subclassed to represent an * object that the application wants to have observed. * <p> * An observable object can have one or more observers. An observer * may be any object that implements interface <tt>Observer</tt>. After an * observable instance changes, an application calling the * <code>Observable</code>'s <code>notifyObservers</code> method * causes all of its observers to be notified of the change by a call * to their <code>update</code> method. * <p> * The order in which notifications will be delivered is unspecified. * The default implementation provided in the Observable class will * notify Observers in the order in which they registered interest, but * subclasses may change this order, use no guaranteed order, deliver * notifications on separate threads, or may guarantee that their * subclass follows this order, as they choose. * <p> * Note that this notification mechanism has nothing to do with threads * and is completely separate from the <tt>wait</tt> and <tt>notify</tt> * mechanism of class <tt>Object</tt>. * <p> * When an observable object is newly created, its set of observers is * empty. Two observers are considered the same if and only if the * <tt>equals</tt> method returns true for them. */ public class Observable { private boolean changed = false; //Vector thread safe private Vector<Observer> obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } //Notify the observer public void notifyObservers(Object arg) { /* * a temporary array buffer, used as a snapshot of the state of * current Observers. */ Object[] arrLocal; //If the changed flag is true, put all observers into an array and set the changed flag to false synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } //Notify all observers in the array to update the information for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } //This is protected, protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); } }
In use, a simple example implements the observer
public class Watched extends Observable { private String state = ""; public String getState() { return state; } //Notify all observers in the setter through setChanged() function and notifyObservers() function public void setState(String state) { if (!this.state.equals(state)) { this.state = state; setChanged(); } notifyObservers(); } }
shortcoming
Observable is a class, and Java does not support multiple inheritance.
It has been discarded in JDK9 because the support for Observer and event model Observable is very limited, the order of sending notifications is unclear, and the status changes do not correspond to notifications one by one.
reference resources
Java design pattern observer pattern (push-pull model)
Observer design mode (II) - push-pull mode
Cartoon: what is "observer mode"?
On Design Pattern -- observer pattern