The Observer Pattern in C#

Definition

Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

How does it work?

Think of a magazine subscription such as Time magazine. You can subscribe to Time magazine. When Time magazine publishes a new edition you automatically get the magazine delivered to your house as long as you are a subscriber. If you chose to unsubscribe, then you will stop getting Time magazine deliveries.

This is basically how the Observer Pattern works except in the example above, Time magazine is referred to as the Subject and the subscriber is referred to as the Observer. There is one Time magazine and many subscribers to that magazine. A one-to-many relationship.

Why is this useful?

  • When we need to notify multiple objects that one object’s state has changed.
  • When we have objects subscribing and unsubscribing from one objects’s state.
  • We can make changes to either the subject or the observer without breaking each other.
  • We never have to modify the subject’s implementation to add a new type of observer.

Example Class Diagram

Example Code

For this example, we are going to mock out a notification system that will notify its clients when it gets a new message.

First, we need to create the interface for Subject. Take another look at the UML Class diagram above for reference if you need to.

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

We also need to create the interface for Observer.

interface IObserver
{
    void Update(string notification);
}

Now for the good stuff. Below you will see that we are creating a class that implements the ISubject interface. Take a close look at the implementation of the three methods in this class.

class NotificationSystem : ISubject {
    private List<IObserver> _observers;

    public NotificationSystem()
    {
        _observers = new List<IObserver>();
    }

    public void RegisterObserver(IObserver o)
    {
        _observers.Add(o);
    }

    public void RemoveObserver(IObserver o)
    {
        _observers.Remove(o);
    }

    public void NotifyObservers(string m) 
    {
        foreach (var observer in _observers)
        {
            observer.Update(m);
        }
    }
}

Above we have a list of Observers that we add to when we register a new observer. We also have the ability to remove the observer and we can notify all observers by iterating over the list of observers and calling the Update() method.

Now that we have a subject, we just need an observer to subscribe to the subject. Let’s do that now.

class Client : IObserver
{
    private ISubject _subject;

    public Client(ISubject subject)
    {
        _subject = subject;
        _subject.RegisterObserver(this);
    }

    public void Update(string notification)
    {
        Console.WriteLine("From Client: " + notification);
    }
}

Above we have a class called Client that implements our IObserver interface. We can see that it is passed in a subject and is subscribing via the RegisterObserver()  method. We also see in the Update()  method we are simply writing to the console.

Now that we have an implementation of the Subject and Observer, it’s time to use them.

static void Main(string[] args)
{
    var notificationSystem = new NotificationSystem();
    var client = new Client(notificationSystem);

    notificationSystem.NotifyObservers("This is my message!");
 }

In the above code, we are creating a new NotificationSystem and then passing that into our new Client. We are then calling the NotifyObservers() method and passing a message. If you run all of the code in this example you will see “From Client: This is my message!” printed to the console.

If we wanted to change the implementation of Update, we could without breaking the Subject. Furthermore, if we wanted to create additional clients we could do so easily without having to modify the implementation of the subject (NotificationSystem)

Push vs Pull

You may have noticed in the example above that we did not create a GetState() on the subject. This is because I wanted to show you how pushing the state works. The Observer Pattern can be implemented two different ways.

  1. Pushing the state – Implemented just like above, passing the state through the Update() method.
  2. Pulling – In this case, we do not pass any date through the update method but rather call the GetState method on the subject from the Observer.

I hope you have found this post helpful in learning about the observer pattern. If so, please share this with your fellow developers so that they too can learn.