Observer

Updated: 03 September 2023

The Observer pattern defines a one to many dependency between objects so that when one object changes state, all it’s dependencies are notfied of this change

The Observer pattern is used to enable one object to subscribe to some changes of another object. It allows us to move from a poll type architecture to a push type architecture

This pattern allows a client (observer) to subscribe to messages/changes from a subject (an observarble), provided the observable is made aware of all observers

Example

A broad idea of the what an observable and observer would contain may look something like this:

1
using System.Collections.Generic;
2
3
namespace DesignPatterns.Observer
4
{
5
// typical structure of an observable
6
public interface IObservable
7
{
8
public List<IObserver> Observers { get; set; }
9
public void Register(IObserver observer);
10
public void Unregister(IObserver observer);
11
public void Notify();
12
}
13
14
// typical structure of an observer
15
public interface IObserver
16
{
17
public void OnNotify();
18
}
19
}

Definition of Classes

An example implementation is seen below:

1
using System;
2
using System.Collections.Generic;
3
4
namespace DesignPatterns.Observer
5
{
6
// typical structure of an observable
7
public interface IObservable<T>
8
{
9
// register an object as an observer
10
public void Register(T observer);
11
//unregister an object as an observer
12
public void Unregister(T observer);
13
// notify observers
14
public void Notify();
15
}
16
17
// typical structure of an observer, implement IDisposable for cleanup
18
public interface IObserver<T>: IDisposable
19
{
20
// handle notification event from observable
21
public void OnNotify(T observable);
22
}
23
24
public class BookingStatus : IObservable<User>
25
{
26
// keep track of the observer list
27
private List<User> _observers = new List<User>();
28
29
// status, we will notify on changes from this
30
private string _status;
31
32
public string Status
33
{
34
get =>_status;
35
}
36
37
public void UpdateStatus (string status)
38
{
39
_status = status;
40
// run notification function when change happens
41
Notify();
42
}
43
44
public void Notify()
45
{
46
// notify all observers
47
_observers.ForEach(o => o.OnNotify(this));
48
}
49
50
public void Register(User user)
51
{
52
if (!_observers.Contains(user))
53
{
54
_observers.Add(user);
55
}
56
}
57
58
public void Unregister(User user)
59
{
60
if (_observers.Contains(user))
61
{
62
_observers.Remove(user);
63
}
64
}
65
}
66
67
public class User : IObserver<BookingStatus>
68
{
69
public string Name { get; }
70
// store a reference to observable for deregistration
71
private BookingStatus _observable { get; set; }
72
73
public User(string name, BookingStatus observable)
74
{
75
Name = name;
76
_observable = observable;
77
_observable.Register(this);
78
}
79
80
// handle notification
81
public void OnNotify(BookingStatus observable)
82
{
83
Console.WriteLine($"User {Name} Status Update: {observable.Status}");
84
}
85
86
public void Dispose()
87
{
88
// unregister when disposed
89
_observable.Unregister(this);
90
}
91
}
92
}

Usage

1
// instantiate observable
2
var bookingStatus = new BookingStatus();
3
4
// instantiate observers
5
var user1 = new User("user1", bookingStatus);
6
var user2 = new User("user2", bookingStatus);
7
8
// update status, notify all observers
9
bookingStatus.UpdateStatus("notification to user1 and user2");
10
11
// dispose user1
12
user1.Dispose();
13
14
// this will only notify user1
15
bookingStatus.UpdateStatus("notification to user1 only");

Using this kind of framework, we can have other classes that extend the IObserver and by creating and registering these we can have different classes that all react to the Notify function calls

In a real implementation you may want to consider using the C# built-in implementation, this makes use of a lot of additional functionality like correct instance disposing, etc. Information on that can be found in the docs

Implementations can differ between different languages/frameworks, you can view implementations in different languages on Refactoring Guru

References