Sei sulla pagina 1di 24

ObserverPattern

KeepingyourObjectsintheKnow!

TheWeatherORama!
Current conditions is
one of three different
displays. The user can
also get weather stats
and a forecast.
Pulls data

Displays

Humidity Sensor
Device

Current
Conditions
Temp: 72
Humidity: 60
Pressure:

Temperature Sensor
Device
Weather Station

Display Device
Pressure Sensor
Device

Weather-O-Rama provides

What we implement

TheJob:CreateanappthatusestheWeatherDataobjecttoupdate

threedisplaysforcurrentconditions,weatherstats,andaforecast.

TheWeatherDataclass
These three methods return the most recent
weather measurements for temperature, humidity,
and pressure respectively.

WeatherData
getTemperature ( )
getHumidity ( )
getPressure ( )
measurementChanged ( )

We dont care HOW these variables are set; the


WeatherData object knows how to get updated
information from the Weather Station

// other methods

/*
A clue: what we need to add!

* This method gets called whenever the


* measurements have been updated.
*/
public void measurementsChanged ( ){
// Your code goes here
}

TheSpecssofar
TheWeatherDataclasshasgettermethodsforthree
measurementvalues:temperature, humidity,andpressure.
ThemeasurementsChanged()methodiscalledanytimenew
weathermeasurementdataisavailable.(Wedontknoworcare
howthismethodiscalled;wejustknowthatitis)
Weneedtoimplementthreedisplayelementsthatusetheweather
data:acurrentconditionsdisplay,astatisticsdisplay,anda
forecastdisplay.Thesedisplaysmustbeupdatedeachtime
WeatherDatahasnewmeasurements.
Thesystemmustbeexpandableotherdeveloperscancreate
newcustomdisplayelementsanduserscanaddorremoveas
manydisplayelementsastheywanttotheapplication.

AFirstMisguidedAttemptatthe
WeatherStation
public class WeatherData {
// instance variable declarations
public void measurementsChanged ( ) {
float temp = getTemperature ( );
float humidity = getHumidity ( );
float pressure = getPressure ( );

Grab the most recent


measurements by calling
the WeatherDatas getter
methods (already
implemented)

currentConditionsDisplay.update (temp, humidity, pressure);


statisticsDisplay.update (temp, humidity, pressure);
forecastDisplay.update (temp, humidity, pressure);
}
// other WeatherData methods here
}

Now update the displays.


Call each display element to
update its display, passing it the
most recent measurements.

Whatswrongwiththefirst
implementation?
public class WeatherData {
// instance variable declarations
public void measurementsChanged ( ) {
float temp = getTemperature ( );
float humidity = getHumidity ( );
float pressure = getPressure ( );

Area of change, we need to


encapsulate this.

currentConditionsDisplay.update (temp, humidity, pressure);


statisticsDisplay.update (temp, humidity, pressure);
forecastDisplay.update (temp, humidity, pressure);
}
// other WeatherData methods here
}
By coding to concrete implementations we
have no way to add or remove other
display elements without making changes
to the program.

At least we seem to be using a common


interface to talk to the display
elementsthey all have an update ( )
method that takes temp, humidity and
pressure values.

TimefortheObserver!
TheNewspaperorMagazinesubscriptionmodel:
Anewspaperpublishergoesintobusinessandbegins
publishingnewspapers
Yousubscribetoaparticularnewspaper,andeverytimethere
isanewedition,itsgetsdeliveredtoyou.Aslongasyou
remainasubscriber,yougetnewnewspapers.
Youunsubscribewhenyoudontwantthenewspapers
anymoreandtheystopbeingdelivered
Whilethepublisherremainsinbusinesspeople,hotels,airlines
etcconstantlysubscribeandunsubscribetothenewspaper.

Publishers+Subscribers=ObserverPattern
The observers have subscribed to the
Subject to receive updates when the
subjects data changes.

Publisher==Subject
Subscribers==Observers
Subject object
manages some data.

New data values are communicated to the


observers in some form when they change.

2
2

int
This object isnt an
observer so it doesnt get
notified when the
subjects data changes.

When data in the Subject


changes, the observers are
notified.

Observer Objects

TheObserverPatternDefined
TheObserverPatterndefinesaonetomanydependencybetween
objectssothatwhenoneobjectchangesstate,allofitsdependentsare
notifiedandupdatedautomatically.
One to many relationship (Subject can have many observers)
Object that holds state

int

Automatic update/notification

Observer Objects

Dependent objects
(observers are
dependent on the
subject to update
them when data
changes.)

ObserverClassDiagram
Heres the Subject interface. Objects use
this interface to register as observers and
also to remove themselves from being
observers.

All potential observers need to


implement the Observer
interface. This interface has just
one method, update ( ), that gets
called when the Subjects state
changes.

Each subject can have


many observers

The concrete subject may also have


methods for setting and getting its state.
A concrete subject always implements the Subject interface. In addition
to the register (attach) and remove (detach) methods, the concrete
subject implements a notify() method to notify observers whenever state
changes.

Concrete observers can be any class that


implements the Observer interface. Each
observer registers with a concrete subject to
receive updates.

DesigningtheWeatherStation
Subject interface

All weather components implement the Observer


interface. This gives the subject a common
interface to talk to when it comes time to update.

<<interface>>
Subject

Create an interface for all display


elements to implement. The display
elements just need to implement a
display ( ) method.

<<interface>>
Observer

observers

registerObservers ( )
removeObservers ( )
notifyObservers ( )

<<interface>>

DisplayElement

update ( )

WeatherData
registerObservers ( )
removeObservers ( )
notifyObservers ( )

CurrentConditions

ForecastDisplay

update ( )
display ( ) { // display
current measurements }

update ( )
display ( ) { // display
the forecast }

StatisticsDisplay

getTemperature ( )
getHumidity ( )
getPressure ( )
measurementsChanged ( )

display ( )

WeatherData now
implements the Subject
interface.

update ( )
display ( ) { // display
avg, min, and max
measurements }

ImplementingtheWeatherStation
public interface Subject {
Both of these methods take an Observer
public void registerObserver (Observer o);
as an argument, that is the Observer to
be registered or removed.
public void removeObserver (Observer o);
public void notifyObservers ( );
This method is called to notify all
}

observers when the Subjects state has


changed.
public interface Observer {
The Observer interface is
public void update (float temp, float humidity, float pressure); implemented by all observers,
so they all have to implement
}
the update ( ) method.

public interface DisplayElement {


public void display ( );
}

The DisplayElement interface


just includes one method,
display ( ), that we will call
when the display element
needs to be displayed.

These are the state values


the Observers get from the
Subject when a weather
measurement changes.

ImplementingtheSubjectInterface
inWeatherData
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 j = observer.indexOf(o);
if (j >= 0) {
observers.remove(j);
}}
public void notifyObservers ( ) {
for (int j = 0; j < observers.size(); j++) {
Observer observer = (Observer)observers.get(j);
observer.update(temperature, humidity, pressure);
}}
public void measurementsChanged ( ) {
notifyObservers ( ); }
// add a set method for testing + other methods.

Added an ArrayList to hold the


Observers, and we create it in the
constructor

Here we implement the Subject Interface

Notify the observers when measurements change.

TheDisplayElements
Implements the Observer and DisplayElement interfaces
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperatue;
The constructors passed the
private float humidity;
weatherData object (the subject)
private Subject weatherData;
and we use it to register the display
as an observer.
public CurrentConditionsDisplay (Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver (this);
}
public void update (float temperature, float humidity, float pressure) {
this.temperature = temperature;
When update ( ) is called, we
this.humidity = humidity;
save the temp and humidity and
call display ( )
display ( );
}
public void display ( ){
System.out.println( Current conditions : + temperature + F degrees and + humidity + %
humidity );
}
}
The display ( ) method just prints out the most recent temp and humidity.

UsingJavasBuiltinObserver
Pattern

JavahasbuiltinsupportinseveralofitsAPIsfortheObserverpattern
Mostgeneral:ObserverinterfaceandtheObservableclassinthejava.util
package.
Observer == Observer
Observable == Subject

The Observable class keeps track of all your


observers and notifies them for you.

Should be familiar -- same as before!

Observable

Observable is
a CLASS not
an interface,
so
WeatherData
extends it!

addObserver ( )
deleteObserver ( )
notifyObservers ( )
setChanged ( )

WeatherData
getTemperature ( )
getHumidity ( )
getPressure ( )

This doesnt
look familiar -we will see it
shortly.
WeatherData
does not need
to implement
register,
remove and
notify -- it
inherits them

<<interface>>
Observer
update ( )

GeneralDisplay
update ( )
display ( )

ForecastDisplay
update ( )
display ( )

StatisticsDisplay
update ( )
display ( )

Some changes to the update ( ) method but


basically the same.

UsingJavasBuiltinObserver
Pattern

ForanObjecttobecomeanObserver:

ImplementtheObserverinterface(thistimethejava.util.Observer
interface)andcalladdObserver()onanyObservableobject.To
removeusedeleteObserver()method.

FortheObservabletosendnotifications

BecomeanObservablebyextendingjava.util.Observablesuperclass
Thena2stepprocess:
1.
2.

FirstcallthesetChanged()methodtosignifythatthestatehaschangedin
yourobject.
ThencalloneoftwonotifyObservers()methods
This takes an arbitrary data

notifyObservers ( )ornotifyObservers(Object arg)

FortheObservertoreceivenotifications

object that gets passed to


each Observer when it is
notified.

Implementtheupdate()methodasbefore.Signatureabitdifferent.
update (Observable o, Object arg)

Subject that send the notification.

ThesetChanged()Method
ThesetChanged()methodisusedtosignifythatthestatehaschangedandthat
notifyObservers(),whenitiscalled,shouldupdateitsobservers.
IfnotifyObservers()iscalledwithoutfirstcallingthesetChanged(),theobserverswillNOT
benotified.

Pseudocode:
setChanged ( ){
changed = true;
}
notifyObservers ( Object arg ){
if (changed ) {
for every observer on the list {
call update(this, arg);

The setChanged() method sets the changed flag to true

}
changed = false;

notifyObserver ( ) only notifies its observers if the


changed flag is true.

}
}
notifyObservers ( ) {
notifyObservers (null);
}

And after it notifies the observers it sets the changed flag back to false.

WhyisthesetChanged()
necessary?
setChanged()methodistheretogiveyoumore
flexibility
Optimizethenotifications
Example:
ifthesensorsaresensitivereadingsmayfluctuatebyafewtenthsofthe
adegree.
Maynotwanttoupdatetheobserverswitheveryfluctuation
setChanged()allowsyoutocontrolthenotificationpoints.

OtherrelevantmethodsinObservable:
clearChanged()
hasChanged()

Thenotify()PushandPull
Methods
public void measurementsChanged ( ) {
setChanged ( );
notifyObservers ( );
}

We first call the setChanged ( ) to


indicate that the state has changed.

We arent sending a data object with the


notifyObservers ( ) call. The Observers are
aware of the subject and they will use that to
pull the latest information from the subject.

public void measurementsChanged ( ){


setChanged ( );
notifyObservers (this);
}

A push method -- the modified data


is being pushed to the observers.

TheDarkSideofthe
java.util.Observable
Doesntthejava.util.ObservableviolateOOdesignprincipleof
programmingtointerfacesandnotimplementations?
Yes it does! Observable is a class and not an interface. Moreover it does
not even implement an interface.
java.util.Observable has a number of problems that limit its usefulness
and reuse
1) Observable is a class: you have to subclass it => you cant add on the
Observable behavior to an existing class that already extends
another superclass. This limits its potential reuse.
2) There isnt an Observable interface so you cannot provide your own
implementation or swap out the Observable implementation with say
a multithreaded implementation
3) Observable protects crucial methods: setChanged () is protected. It
means that you cannot call setChanged unless you have subclassed
Observable. Violates : favor composition over inheritance!

OtherplacestofindObserver
PatterninJava
BothJavaBeansandSwingalsoprovideimplementationsof
theObserverpattern
LookunderthehoodofJButtonssuperclass
AbstractButton
Hasmanyadd/removelistenermethods:allowyoutoaddand
removeobservers(calledlistenersinSwing)
Theselistenersareavailableforvarioustypesofeventsthatoccur
ontheSwingcomponent
Example:ActionListenerletsyoulisteninonanytypesof
actionsthatmightoccuronabuttonlikeabuttonpress.

AlsocheckoutthePropertyChangeListenerinJavaBeans

ASimpleExample:ALife
ChangingApplication
Background:
simpleapplicationwithonebuttonthatsaysShouldI
doit.
Whenyouclickonthebuttonthelistenersgetto
answerthequestioninanywaytheywant.
Codesample:implements2suchlisteners:
AngelListenerandDevilListener

TheLifeChangingCode
public class SwingObserverExample {
JFrame frame;
public static void main (String[] args){
SwingObserverExample example = new SwingObserverExample ( );
example.go ( );
}
public void go ( ){
frame = new JFrame ( );
JButton button = new JButton (Should I do it);
button.addActionListener (new AngelListener ( ) );
button.addActionListener (new DevilListener ( ));
frame.getContentPane().add(BorderLayout.CENTER, button);
// set frame properties here
}
class AngelListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
System.out.println(Dont do it, you might regret it!);
}
}
class DevilListener implements ActionListener {
public void actionPerformed (ActionEvent event) {
System.out.println(Come on, do it!);
}
}

Setting up the listeners/observers of the button.

Here are the class definitions for the


observers, defined as inner classes (they
dont have to be)

Rather than update ( ), the actionPerformed () method


gets called when the state in the subject (button)
changes.

Summary
OOPrincipleinplay:Striveforlooselycoupleddesignsbetweenobjectsthat
interact.
Mainpoints:
TheObserverpatterndefinesaonetomanyrelationshipbetweenobjects
Subjects(observables),updateObserversusingacommoninterface
ObserversarelooselycoupledinthattheObservableknowsnothingaboutthem,other
thantheyimplementtheObserverinterface.
YoucanpushorpulldatafromtheObservablewhenusingthepattern(pullis
consideredmorecorrect)
DontdependonaspecificorderofnotificationforyourObservers
JavahasseveralimplementationsoftheObserverPatternincludingthegeneralpurpose
java.util.Observable
Watchoutforissueswithjava.util.Observable
DontbeafraidtocreateourownversionoftheObservableifneeded
SwingmakesheavyuseoftheObserverpattern,asdomanyGUIframeworks
YoufindthispatterninotherplacesaswellincludingJavaBeansandRMI.

Potrebbero piacerti anche