Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Observer Design Pattern (Delegates/Events)

4.84/5 (23 votes)
9 Aug 2009CPOL3 min read 73.8K   593  
This article uses Delegates/Events to implement an Observer Design Pattern.

Background

In my family, my wife and I both need to work everyday, therefore we drop our kids to the daycare center from 8:00 am to 5:00 pm.

We usually call the daycare a couple times a day to know about our kids, more about their daily activities. If the kids are sick, we contact the kid's doctor to have his medical advice.

Sometimes we found it's not very convenient to make so many calls during work time. However, I'll really appreciate if the daycare could have an automatic system to keep us notified about our kids' daily status. If the kid doesn't feel well, the doctor should start receiving the kid's daily reports as well.

Introduction

The Observer Pattern describes the dependence relationship between one object (observable) to many objects (observers). It's also called Model/View, Dependents, or Distributor/Listener pattern.

This article introduces an implementation when we can use the Observer pattern to keep all observer objects updated after things have changed.

In this case, the kid is an ObservableObject that needs to send out the notification when the daycare updates her daily status. The daycare will determine who will be notified when the daily status is updated.

A kid can be watched by multiple observers, and also one observer can receive multiple updated daily status reports.

Observer Design Pattern Structure

Observer.JPG

Class Diagram

ClassDiagram.jpg

I am a big fan of Delegates/Events, so I decided to create an Event object to handle all the observer calls through multiple delegation in my ObservableObject.

The ObservableObject should be able to allow the client to add/remove observer objects as well. An observer could also receive multiple ObservableObject whenever status is being updated.

Implementation Code

ObservableObject Class

ObservableObject is an abstract base class that implements the methods to interactive with Observers. It allows the client to add/remove an observer to receive the notifications.

The Notify() method in ObservableObject handles the child level events raised. AddObserver and RemoveObserver queue the observer's actions by using muticast delegation.

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

namespace www.askbargains.com.ObserverDesignPatternLib
{
    public delegate void NotifyObserver(string key);

    public abstract class ObservableObject
    {
        public event NotifyObserver NotifyObserverEvent;

        public void AddObserver(NotifyObserver ob)
        {
            NotifyObserverEvent += ob;
        }

        public void RemoveObserver(NotifyObserver ob)
        {
            NotifyObserverEvent -= ob;
        }

        public void Notify(string kidName)
        {
            if (NotifyObserverEvent != null)
            {
                NotifyObserverEvent(kidName);
            }
        }
    }
}

Kid Class

The Kid class is a child class of our ObservableObject. It also contains its own information, such as status. In the DailyStatus property, we will raise Notify event whenever DailyStatus is being updated.

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

namespace www.askbargains.com.ObserverDesignPatternLib
{
    public class Kid : ObservableObject
    {
        public string Name { get; set; }

        private Status _dailyStatus;

        public Status DailyStatus
        {
            get { return _dailyStatus; }
            set
            {
                _dailyStatus = value;
                Notify(this.Name);
            }
        }
    }
}

Parent Class and FamilyDoctor Class

We also create Parent and FamilyDoctor as our observers. DailyStatusUpdate() in the Parent class and ReciveNotes() in the FamilyDoctor class are both local update methods (take only one string parameter), which meet the declaration of the NotifyObserver delegate type in our ObservableObject. That's how we achieve our handshake process between Observer and ObserverableObject.

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

namespace www.askbargains.com.ObserverDesignPatternLib
{
    public class Parent
    {
        private Dictionary<string, Kid> _kids = new Dictionary<string,>();

        public Dictionary<string, Kid> Kids
        {
            get { return _kids; }
            set { _kids = value; }
        }

        public void DailyStatusUpdate(string key)
        {
            Console.WriteLine("Parents received {0}'s daily status. " + 
                              "updated on {1}, Notes: {2} ", 
                              Kids[key].Name, Kids[key].DailyStatus.UpdatedOn, 
                              Kids[key].DailyStatus.Description);
        }
    }
}

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

namespace www.askbargains.com.ObserverDesignPatternLib
{
    public class FamilyDoctor
    {
        private Dictionary<string, Kid> _kids = new Dictionary<string,>();

        public Dictionary<string, Kid> Patients
        {
            get { return _kids; }
            set { _kids = value; }
        }


        public void ReciveNotes(string patientName)
        {
            Console.WriteLine("Family Doctor received {0}'s new daily status. " + 
                              "updates on: {1} . Notes:{2}", 
                              Paitients[patientName].Name, 
                              Paitients[patientName].DailyStatus.UpdatedOn, 
                              Paitients[patientName].DailyStatus.Description);
        }

    }
}

Client App

Once we have our Observer library ready, we can consume it from any application. I am using a console app to demo the client side code.

First, from my client app, a console app will act as the daycare teacher to update the daily status for my kids every 5 seconds.

Second, Aimee and Elizabeth's status will be updated from my side every 5 seconds as well. A short description should let me know what the current status is.

Third, Elizabeth has a family doctor; if she is sick, the daycare teacher has to have her doctor involved to monitor Elizabeth's status as well (she is still a tiny baby). Once she goes back to normal activity, the daycare teacher will remove the doctor from Elizabeth's observer list (so we don't bother the doctor when everything is fine).

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using www.askbargains.com.ObserverDesignPatternLib;

namespace www.askbargains.com.ObserverDesignPatternClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //Two kids been created
            Kid kid1 = new Kid();
            Kid kid2 = new Kid();

            kid1.Name = "Aimee";
            kid2.Name = "Elizabeth";

            //one parent object created. 
            //this parent has two kids in this case. 
            Parent parent1 = new Parent();
            parent1.Kids.Add(kid1.Name, kid1);
            parent1.Kids.Add(kid2.Name, kid2);

            //one family doctor object created
            //this doctor is kid2's family doctor, 
            //and I am going to send the note when kid2 isn't well
            FamilyDoctor doc1 = new FamilyDoctor();
            doc1.Patients.Add(kid2.Name, kid2);

            //I want to send notes to the parents.
            kid1.AddObserver(new NotifyObserver(parent1.DailyStatusUpdate));
            kid2.AddObserver(new NotifyObserver(parent1.DailyStatusUpdate));

            //Update status for both Kids. 
            //Parents will receive the notes at the same time
            kid1.DailyStatus = new Status(String.Format("{0} is happy", kid1.Name));
            kid2.DailyStatus = new Status(String.Format("{0} is fuzzy", kid2.Name));

            //Updates the status after 5 secs
            Thread.Sleep(5000);

            //after 5 secs, kid2 doesn't feel well. need to get doctor involved
            kid2.AddObserver(new NotifyObserver(doc1.ReciveNotes));

            //update two kids' status. 
            //Parent will recive two kids status
            //Doc1 start reciving kid2 's status
            kid1.DailyStatus = new Status(String.Format("{0} is happy", kid1.Name));
            kid2.DailyStatus = new Status(String.Format("{0} is sick. " + 
                                          "Tempture : 39.7", kid2.Name));

            //Updates the status after 5 secs
            Thread.Sleep(5000);

            //update two kids' status
            kid1.DailyStatus = new Status(String.Format("{0} is happy", kid1.Name));
            kid2.DailyStatus = new Status(String.Format("{0} is back to normal. " + 
                                          "she is happy now", kid2.Name));


            //Updates the status after 5 secs
            Thread.Sleep(5000);


            //since kid2 is fine. I am going to deattach the doc1's observation
            kid2.RemoveObserver(new NotifyObserver(doc1.ReciveNotes));

            //update two kids' status
            kid1.DailyStatus = new Status(String.Format("{0} is happy. " + 
                                   "Just had a big lunch", kid1.Name));
            kid2.DailyStatus = new Status(String.Format("{0} is happy. " + 
                                   "Playing with her best friend Kevin", kid2.Name));

            Console.WriteLine("Daily Report End!");
            Console.Read();
        }
    }
}

Once we start our client app, you will see all the messages being printed about my two kids. Cool.

Output.jpg

Conclusion

From this article, I demonstrated how we can use delegation/events to attach observers to an object. All the current messages can be received from the observers when it's attached.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)