Head First Design Patterns – Observer Pattern Using C#

I started re-reading (re-skimming?) Head First Design Patterns this evening to refresh my memory, but got stuck on the first chapter.  Not because I didn’t get the content, but rather I really wanted to see the examples in C#.  First, for anyone who hasn’t read this book, it’s a really well written series of books that are conversational and very visual – there are lots of pictures, diagrams, and examples relating the content to everyday things (good concrete analogies).  This particular book, uses Java.  I don’t.

Back to the Observer pattern.  Obviously this pattern is a first class citizen in .NET, always has been.  A quick search on the interwebs surpringly found no hits for a C# version of the WeatherData example in the book implemented using standard event handlers.  So I gave it a shot because it bugged me. 

First, I created the WeatherData and WeatherDataEventArgs object as follows:

namespace HeadFirstObserverPatternUsingEvents
{
    public class WeatherDataEventArgs : EventArgs
    {
        private float _temperature;
        private float _humidity;
        private float _pressure;

        public WeatherDataEventArgs(float temperature, float humidity, float pressure)
        {
            this._temperature = temperature;
            this._humidity = humidity;
            this._pressure = pressure;
        }

        public float Temperature
        {
            get
            {
                return _temperature;
            }

            set
            {
                _temperature = value;
            }
        }

        public float Humidity
        {
            get
            {
                return _humidity;
            }

            set
            {
                _humidity = value;
            }
        }

        public float Pressure
        {
            get
            {
                return _pressure;
            }

            set
            {
                _pressure = value;
            }
        }
    }

    public class WeatherData
    {
        public delegate void MeasurementsChangedHandler(object sender, WeatherDataEventArgs e);
        public event MeasurementsChangedHandler MeasurementsChanged;

        public void SetMeasurements(float temperature, float humidity, float pressure)
        {
            WeatherDataEventArgs weatherArgs = new WeatherDataEventArgs(temperature, humidity, pressure);

            MeasurementsChanged(this, weatherArgs);
        }
    }
}

The WeatherDataEventArgs class allows the data to be passed into anyone who subscribes (or “Observes”) to the MeasurementsChanged() event.  In other words, the CLR will handle notifying all observers that the event has been raised and will pass the data along in the form of this EventArgs object.  But it’s these two lines that setup the heavy lifting:

        public delegate void MeasurementsChangedHandler(object sender, WeatherDataEventArgs e);
        public event MeasurementsChangedHandler MeasurementsChanged;

We first establish the MeasurementsChangedHandler delegate.  A delegate is sort of like a function pointer – you can treat this sort of like of an object.  In the next line we declare the MeasurementsChanged event; this event must be declared using the same signature of the MeasurementsChangedHandler.  See how that works?  MSDN has a good explanation of this stuff.

Here’s the code for the CurrentConditionsDisplay observer:

    class CurrentConditionsDisplay : IDisplayElement
    {
        private WeatherData _weatherData;
        private float _temperature;
        private float _humidity;

        public float Temperature
        {
            get
            {
                return _temperature;
            }
        }

        public float Humidity
        {
            get
            {
                return _humidity;
            }
        }

        public CurrentConditionsDisplay(WeatherData weatherData)
        {
            this._weatherData = weatherData;
            weatherData.MeasurementsChanged += Update;
        }

        public void Update(object sender, WeatherDataEventArgs we)
        {
            this._temperature = we.Temperature;
            this._humidity = we.Humidity;

            Display();
        }

        public void Display()
        {
            Console.WriteLine("Current conditions: " + this._temperature + "F degrees and " + this._humidity + "% humidity");
        }
    }

Following the convention in the book, CurrentConditionsDisplay implements IDisplayElement, which is a trivial interface with just a Display() call.  In the ctor, we add our Update() method (notice how it’s the same signature as MeasurementsChangedHandler?) to the list of observers of the MeasurementsChanged event with the += operator.  So everytime the WeatherData::SetMeasurements() method is called, it will internally look at it’s list of subscribers and call everyone subscribed, passing them a WeatherDataEventArgs object. 

Next, I didn’t like how the StatisticsDisplay class was implemented in the book’s example.  Why do math when a List<T> can do it for me?  Here’s my implementation:

    class StatisticsDisplay : IDisplayElement
    {
        private WeatherData _weatherData;
        private List<float> _temperatures;

        public StatisticsDisplay(WeatherData weatherData)
        {
            this._temperatures = new List<float>();
            this._weatherData = weatherData;
            weatherData.MeasurementsChanged += Update;
        }

        public void Update(object sender, WeatherDataEventArgs we)
        {
            this._temperatures.Add(we.Temperature);
            Display();
        }

        public void Display()
        {
            String output;

            output = String.Format("Avg/Max/Min temperature = {0}/{1}/{2}", _temperatures.Average(), _temperatures.Max(), _temperatures.Min());
            Console.WriteLine(output);
        }
    }

Looks pretty much exactly the same as the CurrentConditionsDisplay implementation, right?  And with List<T>, it gives me min, max, avg for free.  Not too shabby.

I’ll leave ForecastDisplay and HeatIndexDisplay as an exercise to the reader (all 2 of you).

There it is, the Head First Design Patterns “Observer” pattern implemented in C# using its built in constructs.  Feedback on the code is welcome.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

7 Responses to Head First Design Patterns – Observer Pattern Using C#

  1. jerry says:

    I like to take advantage of the automatic properties to reduce the visual ‘noise’.
    Like this:
    public float Temperature { get; set; }

    You might want to put a size limit on that List… A Queue might be nice, after Enqueue() if you’re over max, throw one away with Dequeue()

  2. jerry says:

    arguably, CurrentConditionsDisplay doesn’t need temp/humidity, since it’s toting around a reference to the WeatherData as well.

  3. nitdoggx says:

    @jerry: All good comments. Regarding the List, it would probably be better for me to actually do the work to save off the min/max, keep a sum and a number of “samples” for calculating average. This would be more performant as it won’t require growth. You’re right in that it may not need temp/humidity, but I wanted to keep it as close to the book’s example as possible. Thanks!

  4. John Ortiz says:

    Hi,

    Thanks for sharing this. I made this, too, understanding the OP; but later (in the same Book, of course) there is an implementation using the native libraries from JDK, they are Observable class and Observer interface (from java.util package). So I guessed if there are equivalents in .NET to accomplish the same task.

    Can you help me with that? I have googled a lot to found a concrete example that following a similar or equal logic that is implemented in java.util package with their well-defined class and interface.

    Thanks in advance.

  5. xahid says:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace ConsoleApplicationobserver
    {
    class Program
    {
    static void Main(string[] args)
    {
    WeatherData weatherdata = new WeatherData();
    currentConditionDisplay currentdisplay = new currentConditionDisplay(weatherdata);
    weatherdata.setMeasurement(80,65,30.4f);
    weatherdata.setMeasurement(82,70,29.2f);
    weatherdata.setMeasurement(78, 90, 29.2f);
    Console.ReadKey();
    }
    }
    public interface subject
    {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObserver();

    }
    public interface Observer
    {
    void update(float temp,float humidity,float pressure);

    }
    public interface DisplayElement
    {

    void display();
    }

    public class WeatherData :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()
    {
    foreach (Observer obs in observers)//i changed a little bit here
    {

    obs.update(temperature, humidity, pressure);

    }

    }
    void measurementChanged()
    {
    notifyObserver();

    }
    public void setMeasurement(float temperature,float humidity,float pressure)
    {
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measurementChanged();

    }

    }
    public class currentConditionDisplay: Observer,DisplayElement
    {
    private float temperature;
    private float humidity;
    private subject weatherData;

    public currentConditionDisplay(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()
    {
    Console.WriteLine(“Current condition:”+temperature+”F degress and”+humidity+”% humidity”);
    }

    }

    }

  6. xahid says:

    The above code works fine.i didn’t do any major change there.that code is almost same as the given code in head first’s book.Statisticks display and Forecast dispaly is not implemented here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s