Translate

Sunday, July 1, 2012

Observer Pattern Tutorial (Introduction to Observer Pattern in C#)


An observer pattern (also called a publisher subscriber pattern) consists of a subject and many observers. The subject notifies the observer in case of a change in state. The image below shows a real life scenario that imitates the observer pattern. Here the recruiter of a company is the subject and the job hunters are the observers.






































In .net, events are an implementation of the observer pattern. 

An example implementing Observer Pattern

 Suppose you want to write an application where any time a particular SQL server goes down, you want certain groups to be notified. The diagram below shows at a high level what we are trying to achieve.



The Subject (the notification service) should be able to

  • Add an observer
  • Remove an Observer
  • Notify an Observer


The Observers (John and Steve's teams) should be able to take the appropriate action when notified.


 Before we proceed let me mention here, it is assumed that the reader is aware of the programming to an interface design principle.

The class diagram to achieve this is shown below.

































This same problem can be solved using the .net standard event pattern



Shown below is the complete code.

using System;
using System.Collections.Generic;
using System.Threading;

public interface INotificationObserver
{
    void OnServerDown();
}

public interface INotificationService
{
    void AddSubScriber(INotificationObserver obs);
    void RemoveSubScriber(INotificationObserver obs);
    void NotifyObserver();
}

internal class Playground
{
    //This is the entry point for the application
    private static void Main(string[] args)
    {
        var ns = new NotificationService();

        var Jo = new JohnsObserver(ns);
        var So = new StevesObserver(ns);

        while (true)
        {
            if (PingSqlServer() == false)
            {
                //When SQL server is down notify observer
                Console.WriteLine("Server down!");
                ns.NotifyObserver();
            }
            else
            {
                Console.WriteLine("All is good");
            }
            Thread.Sleep(1000);
        }
    }

    private static bool PingSqlServer()
    {
        //All this method does is randomly return a false
        var random = new Random();

        int i = random.Next(1, 30);
        if (i > 20)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
}

public class NotificationService : INotificationService
{
    private readonly List<INotificationObserver> _observers;

    public NotificationService()
    {
        _observers = new List<INotificationObserver>();
    }

    public void AddSubScriber(INotificationObserver obs)
    {
        _observers.Add(obs);
    }

    public void RemoveSubScriber(INotificationObserver obs)
    {
        _observers.Remove(obs);
    }

    public void NotifyObserver()
    {
        foreach (INotificationObserver Obs in _observers)
        {
            Obs.OnServerDown();
        }
    }
}


public class JohnsObserver : INotificationObserver
{
    private readonly INotificationService _localRefToNotiServ;

    public JohnsObserver(INotificationService notiServ)
    {
        _localRefToNotiServ = notiServ;
        _localRefToNotiServ.AddSubScriber(this);
    }

    public void OnServerDown()
    {
        //Send email to John
        Console.WriteLine("An email was sent to John");
    }

    public void Unlist()
    {
        _localRefToNotiServ.RemoveSubScriber(this);
    }
}


public class StevesObserver : INotificationObserver
{
    private readonly INotificationService _localRefToNotiServ;

    public StevesObserver(INotificationService notiServ)
    {
        _localRefToNotiServ = notiServ;
        _localRefToNotiServ.AddSubScriber(this);
    }

    public void OnServerDown()
    {
        //Send text message to Steve
        Console.WriteLine("A text was sent to Steve");
    }

    public void Unlist()
    {
        _localRefToNotiServ.RemoveSubScriber(this);
    }
}

  

What is shown above is a push mechanism where in the Subject pushes the update to the Observer. Observer Pattern can also be implemented with a pull mechanism where in the the Observer initiates the Notify mechanism from the Subject.

Further Reading

IObserver<T> Interface
IObservable<T> Interface

No comments:

Post a Comment

Comments will appear once they have been approved by the moderator