CS#7250(Design(Pa1erns(and(
Refactoring(
Instructor: Awais Qasim
Observer(Pa1ern(
Observer'Pa*ern'
This pattern keeps objects in the know when something they care
about happens.
Objects can decide at runtime whether they want to be kept
informed or nit.
It is one of the most heavily used patterns in the Java
3
4
5
Weather'Monitoring'Applica7on'
The three players in the system are the
Weather station (the physical device that acquires the actual
weather data)
WeatherData object (that tracks the data coming from the
Weather Station and updates the displays)
Display that shows users the current weather conditions.
Humidity
Sensor
Pulls displays
Data
Temp Weather Weather Data Display
Sensor Station Object Device
Pressure
Sensor 6
7
8
Unpacking'the'WeatherData'class'
These three getter methods return the most recent weather
measurements for temperature, humidity and pressure respectively.
WeatherData
getTemperature()
getHumidity() /*(
getPressure() *(Call(this(method(
measurementsChanged()
*(whenever(measurements(are(
*(Updated(
*/(
Public(void(measurementsChanged(){(
( //(your(code(goes(here(
Update three }(
different displays
Problem'specica7on'
weatherData class has three
getter methods
measurementsChanged()
method called whenever there
is a change
Three display methods needs to
be supported: current
conditions, weather
statistics and simple
forecast
System should be expandable
First'cut'at'implementa7on'
public class WeatherData {
public void measurementsChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update (temp, humidity, pressure);
statisticsDisplay.update (temp, humidity, pressure);
forecastDisplay.update (temp, humidity, pressure);
}
// other methods
}
First'cut'at'implementa7on'
public class WeatherData {
public void measurementsChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure(); Area of change which can be
Managed better by encapsulation
currentConditionsDisplay.update (temp, humidity, pressure);
statisticsDisplay.update (temp, humidity, pressure);
forecastDisplay.update (temp, humidity, pressure);
}
// other methods
By coding to concrete implementations
} there is no way to add additional display
elements without making code change
Basis'for'observer'pa*ern'
Fashioned after the publish/subscribe model
Works off similar to any subscription model
Buying newspaper
Magazines
Basis'for'observer'pa*ern'
Observer(Pa1ern(Dened(
The Observer Pattern defines a one-to-
many dependency between objects so
that when one object changes state, all of
its dependents are notified and updated
automatically.
Observer'Pa*ern''Class'diagram'
<<interface>> observers <<interface>>
Subject Observer
registerObserver() Update()
removeObserver()
notifyObservers()
ConcreteSubject subject ConcreteObserver
registerObserver() Update()
removeObserver()
notifyObservers()
Observer'pa*ern''power'of'loose'coupling'
The only thing that the subject knows about an observer is
that it implements an interface
Observers can be added at any time and subject need not be
modified to add observers
Subjects and observers can be reused or modified without
impacting the other [as long as they honor the interface
commitments]
Observer'Pa*ern''Weather'data'
<<interface>>
rs
ob serve Observer <<interface>>
<<interface>>
Subject DisplayElement
update()
registerObserver() display()
removeObserver()
notifyObservers()
CurrentConditionsDisplay StatisticsDisplay
WeatherData update() update()
registerObserver() display() display()
removeObserver()
notifyObservers() ForecastDisplay
getTemperature()
getPressure()
getHumidity() update()
measurementsChanged() display()
Add a new type of display named
ThirdPartyDisplay in the current
diagram
Observer'Pa*ern''Weather'data'
<<interface>>
rs
ob serve Observer <<interface>>
<<interface>>
Subject DisplayElement
update()
registerObserver() display()
removeObserver()
notifyObservers()
CurrentConditionsDisplay StatisticsDisplay
WeatherData update() update()
registerObserver() display() display()
removeObserver()
notifyObservers() ForecastDisplay
getTemperature()
getPressure()
getHumidity() update()
measurementsChanged() display()
Weather'data'interfaces'
public(interface(Subject({(
( public(void(registerObserver(Observer(o);(
( public(void(removeObserver(Observer(o);(
( public(void(noMfyObservers();(
}(
public(interface(Observer({(
( public(void(update(oat(temp,(oat(humidity,(oat(pressure);(
}(
public(interface(DisplayElement({(
( public(void(display();(
}(
Implemen7ng'subject'interface'
public(class(WeatherData(implements(Subject({(
( private(ArrayList(observers;(
( private(oat(temperature;(
( private(oat(humidity;(
( private(oat(pressure;(
( (
( public(WeatherData()({(
( ( observers(=(new(ArrayList();(
( }(
( (
Register'and'unregister'
( public(void(registerObserver(Observer(o)({(
( ( observers.add(o);(
( }(
( (
( public(void(removeObserver(Observer(o)({(
( ( int(i(=(observers.indexOf(o);(
( ( if((i(>=(0)({(
( ( ( observers.remove(i);(
( ( }(
( }(
No7fy'methods'
public(void(noMfyObservers()({(
( ( for((int(i(=(0;(i(<(observers.size();(i++)({(
( (( Observer(observer(=((Observer)observers.get(i);(
( (( observer.update(temperature,(humidity,(pressure);(
( ( }(
}(
( (
( public(void(measurementsChanged()({(
( ( noMfyObservers();(
}(
public(void(setMeasurements(oat(temperature,(oat(humidity,(oat(pressure)({(
( this.temperature(=(temperature;(
( this.humidity(=(humidity;(
( this.pressure(=(pressure;(
( measurementsChanged();(
}(
Now,'lets'build'those'display'elements'
public class CurrentConditionsDisplay implements Observer, DisplayElement
{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println(Current conditions: + temperature
+ F degrees and + humidity + % humidity);
}
}
Why we store a reference to the
Subject? It doesnt look like we use
it again after the constructor?
Complete'Weather'Sta7on'
Output'
Observer(pa1ern(
More(analysis(
Push'or'pull'
The notification approach used so far pushes all the state to
all the observers
One can also just send a notification that some thing has
changed and let the observers pull the state information
Java observer pattern support has built in support for both
push and pull in notification. The most general is the
Observer interface and the Observable class in the
java.util package. These are quite similar to our Subject and
Observer interface, but give you a lot of functionality out of
the box.
Java'Observer'Pa*ern''Weather'data'
<<interface>>
rs
ob serve Observer <<interface>>
Observable
DisplayElement
update()
addObserver()
deleteObserver() display()
notifyObservers()
setChanged()
CurrentConditionsDisplay StatisticsDisplay
WeatherData update() update()
getTemperature() display() display()
getPressure()
getHumidity() ForecastDisplay
update()
display()
How'Javas'builtOin'Observer'Pa*ern'works'
For an Object to become an observer.
Implement the Observer interface and call addObserver() on any
Observable object. Likewise, to remove yourself as an observer just call
deleteObserver().
For the Observable to send notifications
First of all you need to be Observable by extending the
java.util.Observable superclass. From there it is a two
step process:
1) You first must call the setChanged() method to signify
that the state has changed in your object
2) Then, call one of two notifyObservers() methods:
either notifyObservers() or notifyObservers(Object
arg)
Reworking'the'Weather'Sta7on'with'the'builtOin'support'
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() { }
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity,
float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
Reworking'the'Weather'Sta7on'with'the'builtOin'support'
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
Rework'the'CurrentCondi7onsDisplay'
public class CurrentConditionsDisplay implements Observer, DisplayElement{
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println(Current conditions: + temperature
+ F degrees and + humidity + % humidity);
}
}
Output'
Problems'with'Java'implementa7on'
Observable is a class
You have to subclass it
You cannot add observable behavior to an existing class
that already extends another superclass
You have to program to an implementation not interface
Observable protects crucial methods
Methods such as setChanged() are protected and not
accessible unless one subclasses Observable.
You cannot favor composition over inheritance.
We may have to roll your own observer interface if Java
utilities dont work for your application
Other'uses'of'the'Observer'pa*ern'in'Java'
GUI interface classes Jbutton
Look at Java API for AbstractButton and Jbutton
It has a lot of add/remove listener methods.
These methods allow us to add and remove observers for
various types of events like a button press.
Summary'so'far..'
Observer pattern Java has several
defines one-to-many implementations of
relationship between observer pattern in
objects util, swing, javabeans
and RMI
We can use push or
pull with observer Swing makes heavy
pattern use of this pattern
Summary(so(far..(
OO(Basics( OO(Pa1erns(
EncapsulaMon( The'Observer'Pa*ern(denes(a(
Inheritance( one#to#many(dependency(
between(objects(so(that(when(
Polymorphism( one(object(changes(state,(all(of(
OO(Principles( its(dependents(are(noMed(and(
Encapsulate(what(varies( updated(automaMcally.(
Favor(composiMon(over( The(Strategy'Pa*ern(denes(a(
inheritance( family(of(algorithms,(
Encapsulates(each(one,(and(
Program(to(interfaces(not(to( makes(them(interchangeable.(
implementaMons( Strategy(lets(the(algorithm(vary(
Strive(for(loosely(coupled(designs( independently(from(clients(that(
between(objects(that(interact( use(it.(