Observer Design Pattern - A Simple Explanation and Implementation..


What is Observer Design Pattern?


It's a design pattern which guides us with a way to design a one to many relationship between a set of objects in such a way that if one of the object (Subject) changes state then all the other objects which depend on it (Observers) will get notified.

Sounds interesting?..
OK..let's take some real life examples to understand it further...
1. Newspaper agency(Subject) has one to many relationship with it's newspaper
    Subscribers(Observers).
2. A Real Estate Agent(Subject) has a one to many relationship with the Property Searchers
    in his contact list.
3. Facebook(Subject) and the facebook account holders(Observers). 
etc etc...

I will pick "Newspaper agency" to explain it further.

Newspaper Agency and The Observer Pattern:

A newspaper agency in your locality is responsible for assuring the timely delivery of Newspapers as per your choice in the morning. If you rollback your memories a bit, you will realize that you have told a vendor to start delivering you a news paper say "Times Of India" or "The Hindu" or some other newspaper of your choice in the morning.

The vendor must have kept a note of your address and the newspaper/magazine choice that you made. Based on this data he has been assuring till date that you get your newspaper before you actually start of with your tea in the morning...:)


courtesy: http://beninblack.com
If we try to dig further the newspaper system then the below main point will definitely pop up.
  1. The agency adds our Name, Address and Choice when we say we want newspapers from them.
  2. In their list, against different names they have different choices of papers.
  3. If there is a update in say newspaper or say there are some add on in the paper, all the customers in the vendors list gets notified.
  4. Also, if you change your location, you tell your vendor to stop delivering you newspapers. That is when he removes your name from his list and you come out of this network.
At this stage the burning question in your mind will definitely be
How is this connected with Observer Pattern?
Ok..go back and read the definition of the Observer Pattern again...fair enough!!

Now you will agree with me when I say that the Newspaper Agency shares a one to many relationship with the Newspaper Customers. We can also call Agency as Subject and Customers as Observers.
That means the Agency(Subject) has a defined way to Add, Remove and Notify Customers and the Customers 

In nut shell the picture below tells the story...

Observer Pattern - One to Many Relation

What are the basic elements of Observer Pattern?
The best way to start explaining this is via the class diagram below.

Class Diagram for Observer Pattern


As you can see in the diagram above, the Observer Pattern comprises of four main elements:-
  1. Subject Interface (ISubject)
  2. Observer Interface (IObserver)
  3. Concrete Subjects (the actual Subjects like Newspaper agency, Property Agent etc) which implements ISubject, and
  4. Concrete Observers which implements IObserver

Now we are all set to see how we actually implement this using a programming language. 
I am using C# but you can use the same logic in any language for that matter.


IObserver

 public interface IObserver
  {
     void Update(IList newspapers, List magazines);
  }

ISubject

 public interface ISubject
 {
     void RegisterObserver(IObserver o);
     void RemoveObserver(IObserver o);
     void NotifyObservers();
 }

IDisplay

This can have any name based upon you requirements. For keeping things simple I am only displaying the newspaper and magazine names for an observer and that's why the name is IDisplay.

 public interface IDisplay
 {
        void Display();
 } 


NewspaperAgency (Subject)


   public class NewspaperAgency : ISubject
    {
        private IList observers;
        private IList newspapers;
        private IList magzines;

        public NewspaperAgency()
        {
            observers = new List();
        }
        
        public void RegisterObserver(IObserver o)
        {
            observers.Add(o);
        }

        public void RemoveObserver(IObserver o)
        {
            int i = observers.IndexOf(o);

            if (i > 0)
                observers.Remove(o);
        }

        public void NotifyObservers()
        {
            for (int i = 0; i < observers.Count; i++)
            {
                IObserver observer = (IObserver)observers[i];
                observer.Update(newspapers, magzines);
            }
        }

        /// 
        /// Triggers when a new newspaper or magzines comes
        /// in the market
        /// 
        public void NewNewsStockArrived()
        {
            NotifyObservers();
        }

        public void UpdateNewsStock(IList newspapers, IList magzines)
        {
            this.newspapers = newspapers;
            this.magzines = magzines;
            NewNewsStockArrived();
        }
    }


CustomerA (Observer)


    public class CustomerA : IObserver, IDisplay
    {
        private IList customerAPapers;
        private IList customerAMagzines;
        private ISubject newsAgencyData;

        public CustomerA(ISubject newsAgencyData)
        {
            this.newsAgencyData = newsAgencyData;
            newsAgencyData.RegisterObserver(this);
        }

        public void Update(IList newspapers, IList magzines)
        {
            this.customerAPapers = newspapers.Where(p => (p == "Times Of India" || p == "The Hindu")).ToList();
            this.customerAMagzines = magzines.Where(m => (m == "India Today" || m == "The Frontline")).ToList();
            Display();
        }

        public void Display()
        {
            Console.WriteLine("CustomerA's Papers:");
            foreach (string p in customerAPapers)
            {
                Console.WriteLine(p);
            }
            Console.WriteLine("\n");
            Console.WriteLine("CustomerA's Magzines:");
            foreach (string m in customerAMagzines)
            {
                Console.WriteLine(m);
            }
        }
    }

Now if we try placing all these stuffs together the complete code will look like the one given below:-

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

namespace ObserverPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            NewspaperAgency nAgency = new NewspaperAgency(); // subject

            CustomerA custA = new CustomerA(nAgency); // observer
            //---
            //more observers can be added below
            //..
            //..
            //..

            nAgency.UpdateNewsStock(new List {"Times Of India", "The Dawn", "The Hindustan Times", "The Hindu"}, 
                                    new List { "India Today", "The Outlook", "Sports Star" });
            Console.Read();
        }
    }


    #region Observer
    public class CustomerA : IObserver, IDisplay
    {
        private IList customerAPapers;
        private IList customerAMagzines;
        private ISubject newsAgencyData;

        public CustomerA(ISubject newsAgencyData)
        {
            this.newsAgencyData = newsAgencyData;
            newsAgencyData.RegisterObserver(this);
        }

        public void Update(IList newspapers, IList magzines)
        {
            this.customerAPapers = newspapers.Where(p => (p == "Times Of India" || p == "The Hindu")).ToList();
            this.customerAMagzines = magzines.Where(m => (m == "India Today" || m == "The Frontline")).ToList();
            Display();
        }

        public void Display()
        {
            Console.WriteLine("CustomerA's Papers:");
            foreach (string p in customerAPapers)
            {
                Console.WriteLine(p);
            }
            Console.WriteLine("\n");
            Console.WriteLine("CustomerA's Magzines:");
            foreach (string m in customerAMagzines)
            {
                Console.WriteLine(m);
            }
        }
    }
    
    #endregion


    #region Subject
    public class NewspaperAgency : ISubject
    {
        private IList observers;
        private IList newspapers;
        private IList magzines;

        public NewspaperAgency()
        {
            observers = new List();
        }
        
        public void RegisterObserver(IObserver o)
        {
            observers.Add(o);
        }

        public void RemoveObserver(IObserver o)
        {
            int i = observers.IndexOf(o);

            if (i > 0)
                observers.Remove(o);
        }

        public void NotifyObservers()
        {
            for (int i = 0; i < observers.Count; i++)
            {
                IObserver observer = (IObserver)observers[i];
                observer.Update(newspapers, magzines);
            }
        }

        /// 
        /// Triggers when a new newspaper or magzines comes
        /// in the market
        /// 
        public void NewNewsStockArrived()
        {
            NotifyObservers();
        }

        public void UpdateNewsStock(IList newspapers, IList magzines)
        {
            this.newspapers = newspapers;
            this.magzines = magzines;
            NewNewsStockArrived();
        }
    } 
    #endregion
    
    #region Interfaces
    public interface ISubject
    {
        void RegisterObserver(IObserver o);
        void RemoveObserver(IObserver o);
        void NotifyObservers();
    }

    public interface IObserver
    {
        void Update(IList newspapers, IList magzines);
    }

    public interface IDisplay
    {
        void Display();
    } 
    #endregion
}


When we try running the above code we get the below output.

Comments

Popular Posts