I've migrated my blog

Thanks for visiting my blog. I am no longer maintaining this and I've migrated the blog to my personal domain . If you would like to follow my blog kindly use my new RSS feed

Wednesday, July 13, 2011

Observer Pattern using Delegates V/s Observer Pattern using Events and Delegates

Most of the articles and texts in C# demonstrate the observer pattern using events and delegates. When going through one such text, a thought come across my mind. Why we are going for events when we can easily achieve the multicasting using Delegates alone. After some R&D, I found it out why and hence this blog post.
I am going to explain this “why” using a Weather Station example. To keep things simple, the Weather Station that we are going to see will keep track of only the temperature. Whenever the temperature changes, it simplify notifies its subscribers with the new temperature. Flash News is one of the subscribers which show the temperature in flash news. News Feed, another subscriber which adds the temperature information to the news feed. Fine, enough theory! It’s time to see the code.
Here is the implementation of Observer Pattern using Delegates alone.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{
    class WeatherStation
    {
        private int _temperature;
        public TemperatureReporter Reporter { get; set; }
        public int Temperature
        {
            get { return _temperature; }
            set
            {
                if (Reporter != null)
                {
                    Reporter(value);
                }
                _tempearture = value;
            }
        }
    }

    class NewsFeed
    {
        WeatherStation weatherStation;

        public void AddTemperatureInfoToFeed(int temperature)
        {
            Console.WriteLine("News Feed: New temperature " + 
                                temperature + " added to feed.");
        }

        public NewsFeed(WeatherStation weatherStation)
        {            
            this.weatherStation = weatherStation;
            weatherStation.Reporter += AddTemperatureInfoToFeed;
        }
    }

    class FlashNews
    {
        WeatherStation weatherStation;

        public void ShowTemperatureInfoInFlashNews(int temperature)
        {
            Console.WriteLine("Flash News: New temperature is " + temperature + ".");
        }

        public FlashNews(WeatherStation weatherStation)
        {            
            this.weatherStation = weatherStation;
            weatherStation.Reporter += ShowTemperatureInfoInFlashNews;
        }
    
    }
}
        
The Main Program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{
    public delegate void TemperatureReporter(int tempearture);

    class Program
    {
        static void Main(string[] args)
        {   
            // Observer Pattern Using Delegates                     
            WeatherStation weatherStation = new WeatherStation();
            FlashNews flashNews = new FlashNews(weatherStation);
            NewsFeed newsFeed = new NewsFeed(weatherStation);        
            weatherStation.Temperature = 30;
            weatherStation.Temperature = 15;
            weatherStation.Temperature = 45;
        }
    }
}
The output

Hurrah! It’s working as expected. Both subscribers are get notified. We can implement the observer pattern using delegates alone. Stop for a minute and go through the code, is the implementation robust? Kudos if you are able to find the shortcomings of this implementation. If not continue reading.
Think of a world where everyone is good. There is no need of police, no need of court either. But it is not the case. You may wonder why I am mentioning this here. This analogy holds true for the objective of this blog post too. There is no need of using events in the c# programming world if all the subscribers are good. Miserably, here also it is not the case. Hence we are going for events instead of using delegates alone.
Drawbacks of using delegates alone:
  • A subscriber can replace other subscribers by reassigning the delegate
  • A subscriber can clear all other subscribers also (by setting the delegate to null).
  • A subscriber can broadcast to other subscribers by invoking the delegate
Clearly all the above 3 drawbacks violates the Observer Pattern.
Here is our evil subscriber code implementation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{
    class EvilSubscriber
    {
        WeatherStation weatherStation;

        public void EvilMessage(int temperature)
        {
            Console.WriteLine("The temperature is " + (temperature * 100));
        }

        public EvilSubscriber(WeatherStation weatherStation)
        {
            this.weatherStation = weatherStation;
            
            // Broadcast to all the subscriber
            weatherStation.Reporter(60);

            // Reassign the subscriber
            weatherStation.Reporter = EvilMessage;

            // Replace all the subscribers
            weatherStation.Reporter = null;

        }
    }
}
    
Hope now you understand the need of a better implementation of observer pattern. Here comes the salvage. Event! Use event along with delegates. With event in place, compiler will not allow the evil subscriber to get compiled.
Robust implementation of Weather Station using events.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{    
    class WeatherStation2
    {
        private int _tempearture;
        public event TemperatureReporter Reporter;
        public int Temperature
        {
            get { return _tempearture; }
            set
            {
                if (Reporter != null)
                {
                    Reporter(value);
                }
                _tempearture = value;
            }
        }
    }

    class NewsFeed2
    {
        WeatherStation2 weatherStation;

        public void AddTemperatureInfoToFeed(int temperature)
        {
            Console.WriteLine("News Feed2: New temperature " + temperature + " added to feed.");
        }

        public NewsFeed2(WeatherStation2 weatherStation)
        {
            this.weatherStation = weatherStation;
            weatherStation.Reporter += AddTemperatureInfoToFeed;
        }
    }

    class FlashNews2
    {
        WeatherStation2 weatherStation;

        public void ShowTemperatureInfoInFlashNews(int temperature)
        {
            Console.WriteLine("Flash News2: New temperature is " + temperature + ".");
        }

        public FlashNews2(WeatherStation2 weatherStation)
        {
            this.weatherStation = weatherStation;
            weatherStation.Reporter += ShowTemperatureInfoInFlashNews;            
        }
    }
}
    
The unfortunate evil subscriber which failed to compile
    class EvilSubscriber2
        {
            WeatherStation2 weatherStation;

            public void EvilMessage(int temperature)
            {
                Console.WriteLine("The temperature is " + (temperature * 100));
            }

            public EvilSubscriber2(WeatherStation2 weatherStation)
            {
                this.weatherStation = weatherStation;

                // Broadcast to all the subscriber
                weatherStation.Reporter(60); // Compiler Error

                // Reassign the subscriber
                weatherStation.Reporter = EvilMessage; // Compiler Error

                // Replace all the subscribers 
                weatherStation.Reporter = null; // Compiler Error  

            }
        }
    
Summary
Critique what you read and analyze.You can download the source code from here

No comments:

Post a Comment