Observer Design Pattern


The Observer design pattern is the most commonly used pattern in the software world. In fact, in the real world also, there are many instances where we follow an observer pattern e.g. subscribing to newspaper edition, the ringing of an alarm to wake up in the morning to go to office, etc.

Let's go straight to the intent; the intent of the pattern is to define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Ok, let's begin to see its implementation. The Observer pattern revolves around subjects and observers. The subject is someone in whose state observers are interested. Taking the example forward, let's say we have a fictitious adventure company called GreatWhiteCompany which is into the business of adventure activities. I once did a rafting expedition through them and seeing their professionalism decided to subscribe to their newspaper. So, here I am an observer that needs to be notified whenever there is a good deal from the company. Now there can be other types of subscribers also who may not be that much interested but still wants to be in the know of things. They also want notification. So let's start coding the classes.

Since we have identified the types of the subjects and observers, we can clearly define 2 interfaces. One interface would be for subjects and another would be for observers. Why use interfaces here? The main purpose of interfaces is to decouple 2 types of objects.

public interface IAdventureCompany
    {
        void Register(IAdventurer adventurer);
        void UnRegister(IAdventurer adventurer);
        void Notify();
    }
    public interface IAdventurer
    {
        void Update();
    }

The important thing to observe here is that the subject classes are interested only in the contract and not in the actual implementation since that is the responsibility of observers themselves. So let's see our subject class:

public class GreatWhiteCompany : IAdventureCompany
    {
        private ArrayList adventurers;
        public GreatWhiteCompany()
        {
            adventurers = new ArrayList();
        }
        public void Register(IAdventurer adventurer)
        {
            adventurers.Add(adventurer);
        }
 
        public void UnRegister(IAdventurer adventurer)
        {
            adventurers.Remove(adventurer);
        }

        public void Notify()
        {
            foreach (IAdventurer adventurer in adventurers)
            {
                adventurer.Update();
            }
        }
    }

The code is very simple to understand. It is to be understood here that we are following a Push model where the subject is pushing the updates to the observers. The pattern can also be implemented using the Pull model where each observer requests the information from the subject. Let's see the implementation now:

//Observer implementation using traditional method
            GreatWhiteCompany raftingSolutions = new GreatWhiteCompany();
            EnthusiasticAdventurer enthusiasticAdventurer = new EnthusiasticAdventurer();
            raftingSolutions.Register(enthusiasticAdventurer);
            AverageAdventurer averageAdventurer = new AverageAdventurer();
            raftingSolutions.Register(averageAdventurer);
            raftingSolutions.Notify();
            Console.ReadLine();
            raftingSolutions.UnRegister(averageAdventurer);
            raftingSolutions.Notify();
            Console.ReadLine();

Running this code will show how each adventurer reacts to the new scheme launched by our fictitious company. Also to be noted is that the Average Adventurer wishes to be removed from the subscriber list and consequently he would not get the updates.

The .NET framework provides a very neat method to implement the Observer pattern through delegates and events.
Behind the scenes, delegates and events do the same work i.e. A subject publishes an event to which any subscriber could attach to. This is done using delegates.

            GreateRaftingCompany greatRaftingCompany = new GreateRaftingCompany();
            EnthusiasticObserver enthusiasticObserver = new EnthusiasticObserver();
            //Lukka oneLukka = new Lukka();
            //enthusiasticObserver.InformLukkaParty += oneLukka.Indulge;
            AverageObserver averageObserver = new AverageObserver();
            greatRaftingCompany.SchemeLaunched += enthusiasticObserver.LetsGoRafting;
            greatRaftingCompany.SchemeLaunched += averageObserver.LetsDecide;
            greatRaftingCompany.PublishNewScheme();
            greatRaftingCompany.SchemeLaunched -= averageObserver.LetsDecide;
            greatRaftingCompany.PublishNewScheme();
            Console.ReadKey();

Basically, the subject class (in this case GreateRaftingCompany) publishes the event "SchemeLaunched" to which EnthusiasticObserver and AverageObserver subscribe. The event internally holds a delegate pointer pointing to all the event handlers which will be invoked when the event will be raised.

Eventually, the event mechanism in .NET is doing the same things as our example above. Also it is to be noted that the event chaining is not limited to Subject to Observers. It's also possible that the observer himself is acting as subject and notifying some other observer. To illustrate this let's assume that the EnthusiasticObserver has some friends who have formed a secret group called Lukka gang and their only purpose in life is to indulge in adventure activities. Now when the rafting company notifies EnthusiasticObserver of the scheme he wants to further notify his Lukka gang. So here after receiving notification, he is acting as a subject and notifying his friends. Now it's a simple matter for his friends in the Lukka gang to subscribe to the event he has published.

public class EnthusiasticObserver
    {
        public  delegate void LukkaIndulgenceHandler(object sender,EventArgs e);
        public event LukkaIndulgenceHandler InformLukkaParty;
        private void OnGoodNews()
        {
            if (InformLukkaParty != null)
                InformLukkaParty(this, null);
        }

        public void LetsGoRafting(object sender, EventArgs e)
        {
            Console.WriteLine("EnthusiasticObserver : We are ready to go whatever the costs");
            OnGoodNews();
        }
    }

Hope this article will help in understating the Observer design pattern and its 
implementation in the context of the .Net framework.

The complete code is listed below:

public interface IAdventureCompany
    {
        void Register(IAdventurer adventurer);
        void UnRegister(IAdventurer adventurer);
        void Notify();
    }
    public interface IAdventurer
    {
        void Update();
    }
    public class EnthusiasticAdventurer : IAdventurer
    {
        public void Update()
        {
            Console.WriteLine("EnthusiasticAdventurer : Lets pack the bags and go.");
        }
    }
    public class AverageAdventurer : IAdventurer
    {
        public void Update()
        {
            Console.WriteLine("AverageAdventurer : Lets work out the expenses and then decide.");
        }
    }

    public class Lukka
    {
        public void Indulge(object sender, EventArgs e)
        {
            Console.WriteLine("Lukka : I am surely going to join");
        }
    }
    public class GreatWhiteCompany : IAdventureCompany
    {
        private ArrayList adventurers;
        public GreatWhiteCompany()
        {
            adventurers = new ArrayList();
        }
        public void Register(IAdventurer adventurer)
        {
            adventurers.Add(adventurer);
        }

        public void UnRegister(IAdventurer adventurer)
        {
            adventurers.Remove(adventurer);
        }
 
        public void Notify()
        {
            foreach (IAdventurer adventurer in adventurers)
            {
                adventurer.Update();
            }
        }
    }

    public class EnthusiasticObserver
    {
        public  delegate void LukkaIndulgenceHandler(object sender,EventArgs e);
        public event LukkaIndulgenceHandler InformLukkaParty;
        private void OnGoodNews()
        {
            if (InformLukkaParty != null)
                InformLukkaParty(this, null);
        }

        public void LetsGoRafting(object sender, EventArgs e)
        {
            Console.WriteLine("EnthusiasticObserver : We are ready to go whatever the costs");
            OnGoodNews();
        }
    }
    public class AverageObserver
    {
        public void LetsDecide(object sender, EventArgs e)
        {
            Console.WriteLine("AverageObserver : Lets decide what is our budget");
        }
    }
    public class GreateRaftingCompany
    {
        public delegate void NewSchemeLaunchHandler(object obj, EventArgs e);
        public event NewSchemeLaunchHandler SchemeLaunched;
        private ArrayList adventurers;
        public GreateRaftingCompany()
        {
            adventurers = new ArrayList();
        }
        public void PublishNewScheme()
        {
            Console.WriteLine("GreateRaftingCompany is giving an inaugral discount of 500 on a one day trip");
            OnNewSchemeLaunched();
        }
        private void OnNewSchemeLaunched()
        {
            if (SchemeLaunched != null)
                SchemeLaunched(this, null);
        }
    }

Up Next
    Ebook Download
    View all
    Learn
    View all