Amazon.com Widgets Delphi and the Observer Pattern

Delphi and the Observer Pattern

By Nick at January 27, 2013 11:46
Filed Under: Delphi, Patterns, Software Development

I’m currently reading Head First Design Patterns, and am finding it very useful and educational.  One problem, though – it’s all in Java.  So I thought that as part of the exercises, I’d translate the code to Delphi.  And also as part of my learning process, I thought it would be a good idea to post an article about each of the patterns.  I also encourage you to buy the book and read it for yourself.

The first real pattern that the book shows is the Observer Pattern.  You should use the observer pattern when you have one object that notifies other objects when events occur.    Or more succinctly, use it when you have an object that needs to notify other objects about stuff that happens. 

HFDP uses the example of a simple weather station that gathers data about the weather.  Each time the weather station takes readings, it needs to update the views of that weather. The weather station reports on Temperature, Humidity, and Pressure.  There are three views that the application provides – current conditions, statistics about the weather, a simple forecast.

The idea here is that the weather station is the subject – it is the object being observed and doing the notifying.  The different displays for the information are the observers – they watch what happens on the weather station and update themselves based on the notifications that they receive from the weather station.  Another way to look at it is that the weather station is a publisher of information, and the displays are subscribers.

The formal definition given for the Observer Pattern goes like this: 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. 

But when it comes time to implement this, the temptation is to simply embed the displays inside of a method of the weather station.  You might create classes for each view, instantiate them in the weather stations constructor, and then simply update the views in a method called MeasurementsChanged.

But of course if you do this, you are breaking some of the base design rules.  First, you’d be coding against an implementation instead of against an interface.  Second, you’ve made it really hard to add a new data display should that become necessary.  Displays are hard-coded into the weather station and can’t be added or removed at runtime.  If we do want to add more displays, we’d need to modify the weather station itself.  Or, put more succinctly, the weather station and its displays are tightly coupled to each other.  And if you know one thing about me, I abhor tight coupling.  And you should too.

In any event, there is a better way – the Observer Pattern.  As noted above, observer pattern consists of a Subject that is monitored by Observers.   We’ll implement a very simple version of the observer pattern. The first thing we will do of course, is declare two interfaces:

type
  IObserver = interface
  ['{69B40B25-B2C8-4F11-B442-39B7DC26FE80}']
    procedure Update(aTemperature: integer; aHumidity: integer; aPressure: Double);
  end;

  ISubject = interface
  ['{A9240295-B0C2-441D-BD43-932AF735832A}']
    procedure RegisterObserver(aObserver: IObserver);
    procedure RemoveObserver(aObserver: IObserver);
    procedure NotifyObservers;
  end;

The first interface is IObserver, which the observing classes will implement.  In it they’ll be updated with all the weather information that the weather station has for us.  The second is ISubject, which will be implemented by the weather station.  It takes three methods, two for handling the connecting and disconnecting of IObservers, and the third for doing the actual notification to the observers. 

The first implementation we’ll look at is the Weather Station.  Here is its implementation declaration.

type
  TWeatherData = class(TInterfacedObject, ISubject)
  private
    FTemperature: integer;
    FHumidity: integer;
    FPressure: double;
    FObserverList: TList<IObserver>;
    function GetTemperature: integer;
    function GetHumidity: integer;
    function GetPressure: double;
  public
    constructor Create;
    destructor Destroy; override;
    procedure SetWeatherInformation(aTemperature: integer; aHumidity: integer; aPressure: double);
    procedure RegisterObserver(aObserver: IObserver);
    procedure RemoveObserver(aObserver: IObserver);
    procedure NotifyObservers;
    procedure MeasurementsChanged;
    property Temperature: integer read GetTemperature;
    property Humidity: integer read GetHumidity;
    property Pressure: double read GetPressure;
  end;

The plumbing for managing the weather implementation is all what you’d expect.  The interesting part, of course, is the implementation of ISubject.  Internally, it uses a TList<IObserver> to manage all the observers that get registered with it.  The RegisterObserver and RemoveObserver methods simply insert and remove, respectively, instances of classes that implement the IObserver interface.

The real action occurs in the NotifyObservers method.  We’ll get to that in a second.  First, though, let’s take a look at the observers.  Since all the displays are very similar, this is a good time to use good old-fashioned inheritance to declare a base class that implements the needed functionality, and then have descendent classes that do the work of providing the specified displays.  Here, then, is the interface for TWeatherDataDisplay:

TWeatherDataDisplay = class(TInterfacedObject, IObserver, IDisplay)
  private
    FSubject: TWeatherData;
    FTemperature: integer;
    FHumidity: integer;
    FPressure: Double;
  public
    constructor Create(aWeatherData: TWeatherData);
    destructor Destroy; override;
    procedure Update(aTemperature: integer; aHumidity: integer; aPressure: Double); virtual;
    procedure Display; virtual; abstract;
  end;

TWeatherDataDisplay has fields to keep track of the current weather information.  It’s descendants can do with it as they please.  It also implements the IDisplay interface so that it can report out what it has to say.  The Update method will allow the Subject – in this case the weather station – to update the displays.  Update is a virtual method, by the way, so that descendants can do different things with the incoming information. 

Okay, so let’s look under the hood.  The weather station can accept and remove IObserver implementers at runtime.   implements the NotifyObservers method as part of the ISubject interface.  It gets called whenever the weather information changes.  It is implemented as follows:

procedure TWeatherData.NotifyObservers;
var
  Observer: IObserver;
begin
  for Observer in FObserverList do
  begin
    Observer.Update(Temperature, Humidity, Pressure);
  end;
end;

This is pretty simple – it merely enumerates over each item in the observer list and calls the Update method, passing along the new weather data.  The base class stores the information, and its descendants process it.

The real “work” gets done when the weather station updates the temperature data and calls NotifyObservers:

procedure TWeatherData.SetWeatherInformation(aTemperature, aHumidity: integer; aPressure: double);
begin
  FTemperature := aTemperature;
  FHumidity := aHumidity;
  FPressure := aPressure;
  MeasurementsChanged;
end;

The entire implementation of our little weather station can be found as part of my demo code on BitBucket.org.

So all of this comes together in a method to create a weather station and add observers:

procedure DoWeatherStation;
var
  WeatherStation: TWeatherStation;
  CurrentDisplay: IDisplay;
  ForecastDisplay: IDisplay;
  StatsDisplay: IDisplay;
begin
  WeatherStation := TWeatherStation.Create;
  try
    CurrentDisplay := TCurrentConditionsDisplay.Create(WeatherData);
    ForecastDisplay := TForecastDisplay.Create(WeatherData);
    StatsDisplay := TStatisticsDisplay.Create(WeatherData);;
    WeatherStation.SetWeatherInformation(70, 55, 28.90);
    WeatherStation.SetWeatherInformation(68, 59, 28.96);
    WeatherStation.SetWeatherInformation(35, 66, 27.40);
    WeatherStation.SetWeatherInformation(55, 55, 27.40);

  finally
    WeatherStation.Free;
  end;
end;

Here are some interesting things to note:

  • This code doesn’t write anything to the console.  All of that is done by the display objects that get registered as observers.  Each observer is registered, and the code pretty much forgets about them.  To the weather station, they are merely IObserver interfaces, and the only thing you can do to an IObserver is call it’s Update method.  From the Weather Station’s perspective, there’s nothing else to it.
  • Four calls to SetWeatherInformation result in for reports from all of the updates to the temperature information.
  • Once you have a reference to a TWeatherStation, you can add or remove displays at runtime.
  • TWeatherStation doesn’t know anything about Weather displays – it only knows about IObservers.  This means that you could have observers that do other things besides being Weather Displays.  You could simply store the weather data, or anything else at all.  For instance, you could create a TWriteWeatherInfoToDatabase class that implements the IObserver interface.  The Weather Station itself neither knows nor cares.  IObservers are under no obligation to do anything specific with the data passed to them. 
  • We can change the observers anytime we want without altering the Subject.  We can add new observers, too.  Basically, observers and subjects are very loosely coupled, and either can be changed, altered, updated, and added to without having to worry about changing the other. 

So that should be a quick rundown on how the observer pattern works.  The obvious weakness here is that our IObserver interface is very specific to weather data.  You can create your own interface for your specific implementation.  The pattern remains the same no matter how the IObserver interface is designed.  This is obviously a good place for generics to enter the picture, and in fact, the Delphi Spring framework implements a generic IObservable<T> interface and implementing class.

So in summation, the Observer pattern ensures that publishing classes (subjects) can communicate updates to their subscribing (observer) classes with very loose and flexible coupling.  Observers can be updated and removed at runtime.  Adding observers requires no change to subjects.  Subjects don’t know much at all about what the observers are up to.  It’s all very easy and elegant and loosely coupled – just like you want it.

Next up – the Decorator Pattern.

blog comments powered by Disqus

My Book

A Pithy Quote for You

"We sleep safe in our beds because rough men stand ready in the night to visit violence on those who would do us harm."    –  George Orwell

Amazon Gift Cards

General Disclaimer

The views I express here are entirely my own and not necessarily those of any other rational person or organization.  However, I strongly recommend that you agree with pretty much everything I say because, well, I'm right.  Most of the time. Except when I'm not, in which case, you shouldn't agree with me.