1. What Observer Pattern defined
The following image is from book Head First Design Patterns.
Points:
- What does this have to do with one-to-many relationship
With the observer pattern, the Subject is the object that contains the state and controls it. So, there is ONE subject with state. The observers, on the other hand use the state, even if they don’t own it. There are many observers and they rely on the Subject to tell them when its state changes. So there is a relationship between the ONE Subject to the MANY Observers. - How does dependence come into this?
Because the subject is the sole owner of the data, the observers are dependent on teh subject to update them when the data changes. This leads to a cleaner OO design than allowing many objects to control the same data.
2. Structure
The UML is shown as follows.
Points:
- Objects use this
ISubject
interface to register as observers and also to remove themselves from being observers. - Each subject can have many observers.
- The
IObserver
interface just has one methodupdate()
that gets called when the subject’s state changes. - A concrete subject implements the
ISubject
interface. Besides the register and remove method, the concrete subject implements anotify()
method which is used to update all the current observers. - The concrete subject may also have methods for setting and getting its state.
- Concrete observers can be any class that implements the
IObserver
interface. Each observer registers with a concrete subject to receive updates.
3. Advantages
The Observer Pattern provides an object design where subjects and observers are loosely coupled. Why?
- The only thing the subject knows about an observer is that it implements a certain interface (the
Observer
interface). It doesn’t need to know the concrete class of the observer, what is does, or anything else about it. - We can add new observers at any time.
- We never need to modify the subject to add new types of observers.
- We can reuse subjects or observers independently of each other
- Changes to either the subject or an observer will not affect the other (power of loosly coupled).
Tip about Loosely coupled
- What: when two objects are loosely coupled, they can interact, but have very little knowledge of each other.
- Advantage: allow us to build flexible OO systems that can handle change because they minimize the interdependency between objects.
4. Example
In this example, a weather station example will introduced. Through subscribing Weather stations in different states, the local residents can get the latest weather condition.
Step 1: Create interfaces
WeatherStation
: the Subject interfacepublic interface WeatherStation { String registerObserver(Observer observer); String removeObserver(Observer observer); void notifyObservers(); }
Observer
: the Observer interfacepublic interface Observer { void update(float temp, float humi, float pres); }
Display
: used to show the weather datapublic interface Display { void displayWeatherData (); }
Step 2: Concrete Subject
DemaciaWeatherStation
: the weather station in Demacia, reports data includes temperature, humidity, pressure.
CODE
public class DemaciaWeatherStation implements WeatherStation{
// some states reflecting the weather in demacia
private float temperature;
private float humidity;
private float pressure;
// observers
private List<Observer> observers;
/*
* constructor
* */
public DemaciaWeatherStation() {
this.observers = new ArrayList<>();
}
/*
* set weather data
* */
public void setWeatherData(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public void measurementsChanged () {
notifyObservers();
}
@Override
public String registerObserver(Observer observer) {
if (observer instanceof DemaciaObserver) {
if (this.observers.contains(observer))
throw new RuntimeException("This observer has already been registered.");
this.observers.add(observer);
return "Success!";
} else {
throw new RuntimeException("You have registered a wrong channel.");
}
}
@Override
public String removeObserver(Observer observer) {
if (observer instanceof DemaciaObserver) {
if (!this.observers.contains(observer))
throw new RuntimeException("This observer does not exist");
this.observers.remove(observer);
return "Success!";
} else {
throw new RuntimeException("Unexpected observer type.");
}
}
@Override
public void notifyObservers() {
for (Observer observer: this.observers) {
DemaciaObserver demaciaObserver = (DemaciaObserver) observer;
demaciaObserver.update(this.temperature, this.humidity, this.pressure);
}
}
}
Step 3: Concrete Observer
DemaciaObserver
: the observer in Demacia.
CODE
public class DemaciaObserver implements Observer, Display {
private float temperature;
private float humidity;
private float pressure;
private WeatherStation weatherStation;
public DemaciaObserver(WeatherStation weatherStation) {
this.weatherStation = weatherStation;
this.weatherStation.registerObserver(this);
}
@Override
public void update(float temp, float humi, float pres) {
this.temperature = temp;
this.humidity = humi;
this.pressure = pres;
displayWeatherData();
}
@Override
public void displayWeatherData() {
System.out.println("Current weather in Demacia is:");
System.out.println("Temperature: " + this.temperature + " ; "
+ "Humidity: " + this.humidity + " ; "
+ "Pressure: " + this.pressure);
}
Final Setp: Let’s test it!
CODE
public class TestObserver {
public static void main(String[] args) {
WeatherStation dWeatherStation = new DemaciaWeatherStation();
DemaciaObserver Garen = new DemaciaObserver(dWeatherStation);
DemaciaObserver Lux = new DemaciaObserver(dWeatherStation);
((DemaciaWeatherStation) dWeatherStation).setWeatherData(35, 2, 1);
((DemaciaWeatherStation) dWeatherStation).setWeatherData(40, 5, 1);
}
}
The output is
From the above result we can see if the local subscribed the weather station, after the weather station notified them, then they could get the latest information.