Observer Pattern

The observer pattern is a design pattern from the Gang Of Four design patterns. And the observer pattern is a behavioral design pattern.

The main intent of this pattern is to define a one-to-many dependency between objects so that when one object changes its state all the objects dependent on it are notified automatically.

In simple language, what the above statement means is creating a notification relationship between one object with other objects who are interested in knowing the state change of that particular object.

The observer pattern is supported out of the box by .NET since its inception. And if you ever work on user interface applications this is something you will definitely use.

The implementation of the observer pattern

For this demonstration, I am going to do a couple of implementations of observer patterns for the .NET framework. The first implementation is using the features of .NET which is available since the inception of the .NET framework. And the second implementation is using a feature that is more recent.

The observer pattern has two main candidates. The first candidate is a subject, which is the main object whose state change is what we want to observe. And the second candidate is the observer(s), and an observer(s) will subscribe to get notification on the state change of the subject.

The state object

Apart from the two main candidates, we will also create a state object. And in our case, we will create a state object User.

namespace ObserverPattern.Demo;
internal class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

The state object user here has two properties, Name, and Age.

The subject of the observer pattern

Using the traditional implementation is the easiest way to implement an observer pattern. And it is through an event and delegate.

The subject defines an event that the observer will subscribe to. And whenever the subject state changes the subject will raise the event and observers will get a callback. And that is the implementation we are going to do first.

We will create an ISubject interface, which will define an event with the name UserChanged, and its type will be Action<User>. And it will also define a method UpdateUserAge, which will return an integer and will take a parameter age of type integer.

namespace ObserverPattern.Demo;

internal interface ISubject
{
    event Action<User> UserChanged;

    void UpdateUserAge(int age);
}

internal class Subject : ISubject
{
    private readonly User user;
    public event Action<User> UserChanged;

    public Subject(string name, int age)
    {
        user = new User { Name = name, Age = age };
    }

    public void UpdateUserAge(int age)
    {
        user.Age = age;
        if(UserChanged != null)
            UserChanged(user);
    }
}

For the Subject class, I will implement the ISubject interface. And I will declare a User variable. And in the constructor of the class will take the name of the user and the age as a parameter. Next, I will create a User object using the name and the age, and assign it to the local user variable.

Finally, inside the UpdateUserAge method, I will set the age of the user object with the age parameter of the method. Then I will check if the UserChanged event is configured by checking if it is not null. And if it is not null I will invoke the UserChanged event by passing the user object.

And here as you can see the Subject is now raising an event when its internal state changes.

The observer of the observer pattern

The implementation of the observer will be very simple. In the constructor for the observer, we will take the ISubject as a dependency. And inside the constructor, I will subscribe to the UserChanged event. Inside the delegate attached to the event, I will write the user information on the console.

namespace ObserverPattern.Demo;

internal class Observer 
{
    public Observer(ISubject subject)
    {
        subject.UserChanged += Subject_UserChanged;
    }

    private void Subject_UserChanged(User obj)
    {
        Console.WriteLine($"Name: {obj.Name}, Age: {obj.Age}");
    }
}

Next inside the program class, I will create an instance of the Subject. And after that, I will create an instance of the observer passing the instance of the Subject in the constructor.

Finally, I will call the UpdateUserAge method on the subject passing 25 as the age. I will add a small delay in the end since this is an asynchronous process, I want to make sure the observer has enough time to get the callback.

using ObserverPattern.Demo;

var subject = new Subject("Bob", 20);
var observer = new Observer(subject);

subject.UpdateUserAge(25);
await Task.Delay(1000);

This is a very simple implementation of the observer pattern using an event and delegate. Next, I will run this program and once I run this program I can see the name and age of the user in the console output.

observer pattern

Implementation using IObservable

Now I will change the implementation of the subject and the observer. Instead of using event and delegate, I am going to use the interfaces which come out of the box with the .NET framework. And these interfaces are IObservable and IObserver.

Change in Subject

The Subject class will now implement the IObservable interface. And it will also implement the IDisposable interface so it can clear all observers on the disposal of the object.

I will remove the ISubject interface, and along with that, I will also remove the UserChanged event.

Next, I will create a List of IObserver in the class, which I will use to keep a list of observers who will want to subscribe to the change for the subject.

namespace ObserverPattern.Demo;

internal class Subject : IObservable<User>, IDisposable
{
    private readonly User user;
    private IList<IObserver<User>> observers = new List<IObserver<User>>();

    public Subject(string name, int age)
    {
        user = new User { Name = name, Age = age };
    }

    public void Dispose()
    {
        observers.Clear();
    }

    public IDisposable Subscribe(IObserver<User> observer)
    {
        this.observers.Add(observer);
        observer.OnNext(user);
        return this;
    }

    public void UpdateUserAge(int age)
    {
        user.Age = age;
        foreach (var observer in observers)
        {
            observer.OnNext(user);
        }
    }
}

And for the implementation of the Subscribe method of the IObservable, I will add the IObserver to the list of observers. And after that, I will call the OnNext method of the IObserver passing the User object.

Next, I will change the implementation of the UpdateUserAge, and inside that instead of calling the event, for each observer in the observer list I will call OnNext passing the user object.

Finally, inside the Dispose method of the IDispossable, I will just call the Clear method of the observer list to remove all observers.

The OnNext method is the implementation of IObserver, which is the callback method. So every time the user object is updated, the implementation of the IObserver will get a callback.

Change in Observer

I will now change the Observer to implement the IObserver interface and I will remove the current body of the class.

Next, I will complete the implementation of the interface. For all three methods, OnCompleted, OnError, and OnNext, I will just write a string to the console. Of course in a real-life scenario, these methods will contain business logic. But for this demonstration, the console log is going to be sufficient to demonstrate the concept.

namespace ObserverPattern.Demo;
internal class Observer : IObserver<User>
{
    public void OnCompleted()
    {
        Console.WriteLine("Completed");
    }

    public void OnError(Exception error)
    {
        Console.WriteLine("Error");
    }

    public void OnNext(User value)
    {
        Console.WriteLine($"Name: {value.Name}, Age: {value.Age}");
    }
}

Change in Program class

Next, I will update the Program class with a couple of minor changes.

using ObserverPattern.Demo;

var subject = new Subject("Bob", 20);
var observer = new Observer();
subject.Subscribe(observer);

subject.UpdateUserAge(25);
await Task.Delay(1000);

Inside the Program class, firstly, the observer no longer takes any constructor parameter, so I will change that.

Secondly, I will call the Subscribe method of the Subject passing the observer as a parameter.

Now we will run the function and we will see the console is printing out the user object twice. The first time when we Subscribe and the second time when we change the age.

observer pattern with IObservable

Conclusion

So these are the two very simple implementations of the observer design pattern. Apart from that we can, of course, use the reactive extensions which internally use the observer design pattern. In a nutshell, this is how we can implement observer design patterns using the .NET framework.

A Youtube video for this implementation is available here.