Observer Design Pattern
You can see this and other great articles on design patterns here.
Observer Design Pattern allows you to have a publisher-subscriber framework where a change to a publisher will notify all of its subscribers automatically. The subscribers are registered to the publisher so that when a change occurs in the publisher all of the subscribers are notified. The publishers and the subscribers are decoupled through the use of interfaces so that the development of each can vary independently.
The
There are 2 parts in the observer pattern:
- The first are the subjects. They are the publishers. When a change occurs to a subject it should notify all of its subscribers.
- The second are the observers. They are the subscribers. They simply listen to the changes in the subjects.
The subjects are the publishers and the observers are the subscribers. It's just a different terminology. Below is the UML of the Observer Design Pattern, the left part are the subjects, and the right part are the observers:
- The
ISubject
is the interface that all publishers implement and has the following properties and methods:
observers
-- List of observers that listen to the changes in the subject
Attach(IObserver)
-- Adds an observer to listen to changes in the subject
Detach(IObserver)
-- Remove an observer from listening changes in the subject
Notify()
-- Send updates to all the observers that subscribed to it
- The
ConcreteSubject
is the publisher class and it implements theISubject
interface. Besides the implementation of theISubject
interface it also has thesubjectState
variable:
subjectState
-- the variable that represents the state of the subject
- The
IObserver
is the interface that all subscribers implement and has theUpdate
method:
Update()
-- update the subscriber and is called by the subject (publisher)
- The
ConcreteObserver
is the subscriber class and it implements theIObserver
interface. Below are its variables and methods:
observerState
-- the variable that represents the state of the observer
Update()
-- update the state of the observer. Notice that the method simply assigns theobserverState
variable from the subject's state. Therefore when a change to the subject's state occurs, the observer's state will become the same as the subject's state.
Notice in the Update
method of the ConcreteObserver we assign the observerState
variable as the subject's state and we only have one observerState
variable. This means that the observer pattern is a one-to-many relationship, where one subject can have many observers listening to the subject's change but not vice versa.
A comparison between the mediator pattern and the observer pattern shows some similarities and some clear differences. Both patterns facilitates the communication between objects, and both decouples the link between the sender and the receiver. The main difference is that in the mediator pattern there is the notion of the participants and they communicate with each other using the mediator as a central hub, whereas in the observer pattern there is a clear distinction between the sender and the receiver, and the receiver merely listens to the changes in the sender.
While there are many different ways to implement the observer pattern, such as using delegates and events or the IObserver<T>, the concepts are all the same. That is, the observers are registered to listen to the changes in the subject and are notified when the subject changes. For the purpose of demonstrating the concept of the observer pattern we will not dig into the technicalities of the multiple ways to implement the pattern, but simply show how the observer pattern works by demonstrating the concept.
Below are the implementation code and the output of the Observer Design Pattern. Notice that a change to the subject automatically updates all of its observers:
class Program { static void Main(string[] args) { //one subject and two observers ISubject<string> subject = new ConcreteSubject<string>(); IObserver<string> observer1 = new ConcreteObserver<string>("observer1"); IObserver<string> observer2 = new ConcreteObserver<string>("observer2"); //register the observers to the subject subject.Attach(observer1); subject.Attach(observer2); //a change to the subject automatically notifies all the observers subject.SetState("stateX"); } } public interface ISubject<t> { List<iobserver><t>> Observers { get; } void Attach(IObserver<t> i); IObserver<t> Detach(IObserver<t> i); void Notify(); T GetState(); void SetState(T state); } public class ConcreteSubject<t> : ISubject<t> { private List<iobserver><t>> Observers = new List<iobserver><t>>(); private T SubjectState; List<iobserver><t>> ISubject<t>.Observers { get { return Observers; } } void ISubject<t>.Attach(IObserver<t> i) { Observers.Add(i); } IObserver<t> ISubject<t>.Detach(IObserver<t> i) { Observers.Remove(i); return i; } void ISubject<t>.Notify() { foreach (IObserver<t> o in Observers) o.Update(SubjectState); //update the observer } T ISubject<t>.GetState() { return SubjectState; } void ISubject<t>.SetState(T state) { SubjectState = state; ((ISubject<t>)this).Notify(); //notify the observers of the change } } public interface IObserver<t> { void Update(T subjectState); //T GetState(); } public class ConcreteObserver<t> : IObserver<t> { private T observerState; private string name; public ConcreteObserver(string name) { this.name = name; } void IObserver<t>.Update(T subjectState) { observerState = subjectState; Console.WriteLine(this.name + " is now " + this.observerState); } }
Liked this article? You can see this and other great articles on design patterns here.