Observer Pattern - eprg.org · Observer Pattern • Observer defined • The Observer Pattern...

Preview:

Citation preview

Observer PatternSteven R. Bagley

Introduction

• Another new pattern

• Already seen

• Strategy

• Decorator

• The Observer Pattern

Weather Monitoring

SCI showed how weather information could be transmitted

Weather Station

• Various sensors recording

• Temperature

• Pressure

• Humidity

• Periodically send data to central computer

• Need software to handle data

See SCI for details of how you might encode the data…

Software

• Clients want three displays

• Current data

• Statistics

• Forecast

• Displays must update automatically

Break into objects

• Object to represent the weather station

• Handles receiving data from sensors

• Accessor methods to get data

• Ignore hardware side, assume object always returns latest live data

• Displays can call methods to get data

We are not interested in how the data is fetched -- probably the object creates a thread that talks to the hardware or sets up interrupt handlers. Irrelevant for this discussion.

Display

• Class for each display

• Each Class has a reference to the WeatherStation object

• Display gets data from the WeatherStation object

• The question is when…

GetTemperature()GetPressure()GetHumidity()

// other methods

WeatherStationDisplay()weatherStationForecastDisplay

Display()weatherStationStatisticsDisplay

Display()weatherStationCurrentDataDisplay

Updates

• Current Data Display is easy, regularly refresh display

• Statistics and Forecast more tricky

• Data could be updated at any point

• If any is missed then the statistics aren’t correct and the forecast less accurate

Polling

• Forecast and Stats. Display objects will need to poll continuously

• Use a lot of CPU time

• Inefficient

• How can we tell the data is new?

Push or Pull

• This is a pull model

• We ‘pull’ the data from the WeatherStation object when we need it

• No way of knowing if and when it has changed

Push Model

• Only thing that knows that the data has changed is WeatherStation

• Turn things around

• Rather than the displays polling WeatherStation

• WeatherStation tells the displays that things have changed

GetTemperature()GetPressure()GetHumidity()

MeasurementsChanged()

// other methods

WeatherStationDisplay()Update()

ForecastDisplay

Display()Update()

StatisticsDisplay

Display()Update()

CurrentDataDisplay

Changes

• WeatherStation now has references to displays

• New MeasurementsChanged method

• Called internally whenever new values received from the sensors

• Dispatches messages to the display objects

classs CWeatherStation{public:double GetTemperature();double GetPressure();double GetHumidity();

private:void MeasurementsChanged();

/* various other methods */};

void CWeatherStation::MeasurementsChanged();{double t = GetTemperature();double h = GetHumiditiy();double p = GetPressure();

m_currentConditionsDisplay->Update(t, h, p);m_statisticsDisplay->Update(t, h, p);m_forecastDisplay->Update(t, h, p);}

void CWeatherStation::MeasurementsChanged();{double t = GetTemperature();double h = GetHumiditiy();double p = GetPressure();

m_currentConditionsDisplay->Update(t, h, p);m_statisticsDisplay->Update(t, h, p);m_forecastDisplay->Update(t, h, p);}

Coding to concrete implementation what if

we need to change it

void CWeatherStation::MeasurementsChanged();{double t = GetTemperature();double h = GetHumiditiy();double p = GetPressure();

m_currentConditionsDisplay->Update(t, h, p);m_statisticsDisplay->Update(t, h, p);m_forecastDisplay->Update(t, h, p);} {At least we seem to be

using a common interfaceCoding to concrete implementation what if

we need to change it

How are we doing?

• Better solution — removes the need for displays to poll WeatherStation

• Tied to a specific set of displays

• How do we add/remove displays without recompile?

• Could we use a Strategy Pattern here?

Strategy not really appropriate, the behaviour isn’t really changing, it’s just who is being notified that changes. We don’t want a class explosion for each different group of objects that might be listening.

Newspapers are published and people can opt into having them delivered to them and at a later time stopThe Observer pattern works in the same model the subject being observed has a group of subscribers that it ‘publishes’ the information too.

Here is the news…

• The WeatherStation has information it wants to share

• But it does not need to know who it is giving it to

• Need a mechanism that allows WeatherStation to publish the information to whoever is listening for it

Observer Pattern

• Identical to newspaper delivery (minus the paper boy)

• Object publishing the information is called the Subject

• Objects subscribing to the information are called Observers

Bagpuss

Bagpuss subject sends information out, received by Charlie, Lizzie and EddieYaffle doesn’t get it (he’s not registered to receive it)

Charlie

Lizzie

Eddie

Bagpuss

Bagpuss subject sends information out, received by Charlie, Lizzie and EddieYaffle doesn’t get it (he’s not registered to receive it)

Charlie

Lizzie

Eddie

Yaffle

Bagpuss

Bagpuss subject sends information out, received by Charlie, Lizzie and EddieYaffle doesn’t get it (he’s not registered to receive it)

Charlie

Lizzie

Eddie

Yaffle

Bagpuss

Bagpuss subject sends information out, received by Charlie, Lizzie and EddieYaffle doesn’t get it (he’s not registered to receive it)

Charlie

Lizzie

Eddie

Yaffle

Bagpuss

Yaffle decides he wants in on the action so registers himself with BagpussBagpuss sends out new data which they all receiveEddie’s fed up with the data so unregisters himselfBagpuss then sends out more datawhich Eddie doesn’t receive

Charlie

Lizzie

Eddie

YaffleBagpuss

Yaffle decides he wants in on the action so registers himself with BagpussBagpuss sends out new data which they all receiveEddie’s fed up with the data so unregisters himselfBagpuss then sends out more datawhich Eddie doesn’t receive

Charlie

Lizzie

Eddie

YaffleBagpuss

Yaffle decides he wants in on the action so registers himself with BagpussBagpuss sends out new data which they all receiveEddie’s fed up with the data so unregisters himselfBagpuss then sends out more datawhich Eddie doesn’t receive

Charlie

Lizzie

Eddie

YaffleBagpuss

Yaffle decides he wants in on the action so registers himself with BagpussBagpuss sends out new data which they all receiveEddie’s fed up with the data so unregisters himselfBagpuss then sends out more datawhich Eddie doesn’t receive

Charlie

Lizzie

Eddie

YaffleBagpuss

Yaffle decides he wants in on the action so registers himself with BagpussBagpuss sends out new data which they all receiveEddie’s fed up with the data so unregisters himselfBagpuss then sends out more datawhich Eddie doesn’t receive

Observer Pattern

• Observer defined

• 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

Charlie

Lizzie

Eddie

2

2

2

YaffleBagpuss

2

Charlie

Lizzie

Eddie

2

2

2

YaffleBagpuss

2

Objectholding state

Charlie

Lizzie

Eddie

2

2

2

YaffleBagpuss

2

Objectholding state

One-to-many Relationship

Charlie

Lizzie

Eddie

2

2

2

YaffleBagpuss

2

Dependentobjects

Objectholding state

One-to-many Relationship

Charlie

Lizzie

Eddie

2

2

2

YaffleBagpuss

2

Automaticupdate

Dependentobjects

Objectholding state

One-to-many Relationship

Observer defined

• One-to-Many

• One object holds the data – the Subject

• Many recipients — the Observers

• Observers dependent on the Subject to inform them of changes in the data

RegisterObserver()RemoveObserver()NotifyObserver()

<<interface>>Subject

RegisterObserver()RemoveObserver()NotifyObserver()

GetState()SetState()

ConcreteSubject

Update()

<<interface>>Observer

Update()

// other Observer Specific methods

ConcreteObserver

*

1

Loose Coupling

• Loose Coupled objects interact, but have little knowledge of each other

• Observer Pattern is an example of a loosely coupled OO design

Loose Observation

• Subject only knows that Observers implement an interface

• Can add new observers at any point

• New observer types don’t need modification of the Subject

• Changes to subject or observer don’t affect the other

Design Principle

“Strive for loosely coupled designs between objects that interact.”

Weather Observation

• WeatherStation has the state that’s changing so we make this the subject

• Displays need to be notified, so we make each of these observers

• Give displays an Update() method that gets called with the data as parameters

RegisterObserver()RemoveObserver()NotifyObserver()

<<interface>>Subject

RegisterObserver()RemoveObserver()NotifyObserver()

GetTemperature()GetHumidity()GetPressure()MeasurementsChanged()

WeatherStation

Update()

<<interface>>Observer

*

1

Display()

<<interface>>Window

Update()Display()

CalculateForecast()

ForecastDisplay

Update()Display()

CurrentConditionsDisplay

Update()Display()

StatisticsDisplay

Weather Station App

• Create WeatherStation object

• Create the display objects

• Register the display objects with WeatherStation object

Implementing Subject

• How do we implement the Subject?

• Need to manage a collection of objects

• Arrays unsuitable — Fixed Size, need to shift values around

• Linked List better choice

• Example here will use an array for simplicity

classs CWeatherStation : public ISubject{public: void RegisterObserver(IObserver *o); void RemoveObserver(IObserver *o); void NotifyObservers(); double GetTemperature(); double GetPressure(); double GetHumidity();

private: void MeasurementsChanged();

IObserver *m_observers[10]; int m_cObservers;};

class IObserver{public: virtual void Update(double temperature, double humidity, double pressure) = 0;}

Registering Observers

• Simple

• Add the observer to the list

• Probably should check it is not in the list already

void CWeatherStation::RegisterObserver(IObserver *o){ if(o != NULL && m_cObservers < 9) { m_observers[m_cObservers++] = o; }}

Notify Observers

• Again, simple

• Step through the list calling each observer’s update method

Remove Observer

• Slightly more complicated

• Need to find node (if present) containing the observer

• Then Delete it from the list

Observer Update

• What about Update()?

• Update the display status

• Call Display() method to redraw the screen

Push Data or Pull Data

• Our WeatherStation is pushing the data at the objects with the notification that it has changed

• Would it be better to notify the observer and let it pull what it needs?

• It depends

Amount of State

• In our example the amount of state is tiny and so it does make sense to push it

• If however there is a lot of state to pass over then it is probably best to just notify that it has changed

• Observer can then get what it needs

Summary

• Observer Pattern

• A one-to-many relationship

• Where a subject notifies many observers that it’s state has changed

Recommended