The observer Pattern
=================
Most of time we will be in need of making some systems which work like the news paper which means that we will have one object which will need to give periodically information to the client. So, the Observer Pattern = Publishers (Subject) + Subscribers (Observer).
To deal with this form of design we have two choices. The first one is to write by ourselves interfaces to represent the subject and the observer class the second one is to use API’s tools which are respectively java.util.observable class to represent subject and java.util.observer interface to represent observer. But this last one has a dark side: Observable is a class and don’t even have an interface and we know that in design pattern it is advised to use interface as the principle “Favor composition over inheritance” said. Worse is that the method setChanged() is a protected one which means that we can’t call the method unless we has subclassed Observable.
Here we will use code to understand more about it, in the first example we created our own Subject and Observer interface, in the second one we used one’s given by API. This example is about a weather station which will get data from the subject and display the data. Here we can remember that a weather station needs to update its data each time there is changes.
Example 1:
package com.observer.weather;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
package com.observer.weather;
public interface Observer {
public void update(float temperature, float humidity, float pressure);
}
package com.observer.weather;
import java.util.ArrayList;
/*
* This class will be used to deal with observes
*/
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers = new ArrayList();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i>= 0){
observers.remove(i);
}
}
public void notifyObserver() {
for(int i = 0; i<observers.size(); i++){
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void measurementsChanged(){
notifyObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
package com.observer.weather;
public interface DisplayElement {
public void display();
}
package com.observer.weather;
public class CurrentConditionsDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("The current conditions: "+temperature+
" F degrees, "+humidity+" % humidity and "+pressure+" of pressure ");
}
}
package com.observer.weather;
public class CurrentConditionsDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("The current conditions: "+temperature+
" F degrees, "+humidity+" % humidity and "+pressure+" of pressure ");
}
}
package com.observer.weather;
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
HeatIndexDisplay heatdisplay = new HeatIndexDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 7, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
Example 2:
package com.observer.weather2;
import java.util.Observable;
/*
* This class will be the subject of the weather station
*/
public class WeatherData extends Observable{
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){}
/*
* Will be called whenever data change
*/
public void measurementsChanged(){
setChanged();
notifyObservers();
}
public void setMesurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
package com.observer.weather2;
public interface DisplayElement {
public void display();
}
package com.observer.weather2;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
@Override
public void display() {
System.out.println("The current conditions: "+temperature+" F degrees, "+humidity+" % humidity");
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData)o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
}
package com.observer.weather2;
import java.util.Observable;
import java.util.Observer;
public class ForecastDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float currentPressure = 29.2f;
private float lastPressure;
private Observable observable;
public ForecastDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
public void display() {
System.out.println("The current conditions: "+temperature+" F degrees, "+humidity+" % humidity");
}
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData)o;
lastPressure = currentPressure;
currentPressure = weatherData.getPressure();
temperature = weatherData.getTemperature();
humidity = weatherData.getHumidity();
display();
}
}
}
package com.observer.weather2;
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMesurements(80, 65, 30.4f);
weatherData.setMesurements(82, 7, 29.2f);
weatherData.setMesurements(78, 90, 29.2f);
}
}
Conclusion:
----------------
In conclusion we learnt a new principle which is: “Strive for loosely coupled design between object that interact”. This principle will make our program more flexible that means for instance if we need to add a new Display of the weather data or need to change how one display tool could display those data.
Remember that the
Observer pattern defines a one- to- many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.