This has been a long overdue. I had started with this series of byte sized tutorials on various patterns but never went ahead and completed the series. So here am continuing from where I left with the Observer Pattern.
While working with applications, there would times you could have an object which would like to notify other interested object about a change in its state. The interested subscribers could have been implemented in such a way that it sends indefinete amount of request asking the subject if it has changed. But that would not be ideal. Instead, you would want the subject to notify the subscribers every time the state change occurs, thereby avoid the need for the innumerous ‘Is there a state change’ calls.
Imagine how subscribers subscribe to newspaper. As a consumer, one would subscribe a particular newspaper publisher and would be delivered the print each time there is a new one out there. What you do with you print and how you makes use of it is upon the each subscriber, but one thing is common – if you are subscribed to the publisher, you would be notified and given your copy when a new one is out.
This is the princple on which observer pattern works. It ensure an implementation where subscribers and observers can remain loosely coupled while providing the subject the ability to notify the subscribers of state changes.
Let us first have a look at the class diagram and overall summary of the observer pattern.
The core participants of the observer pattern are
- ISubject
- ConcreteSubject
- IObserver
- ConcreteObservers
The abstract Subject contains methods which can be used by observers to register/unregister themselves. The subject on other hand, maintains a collection of all the observers who have registered themselves with it. It would then use the Update
method (part of IObserver) of the observers to notify them of a state change. Let us go ahead and implement the abstract and concrete observers.
// Abstract Subsccriber
public abstract class SubscriberBase
{
protected readonly INewsPublisher _newsPublisher;
protected WeakReference<SubscriberBase> _weakReference;
public SubscriberBase(INewsPublisher publisher)
{
_newsPublisher = publisher;
}
public abstract void Update(string message);
public void Subscribe()
{
_weakReference= _newsPublisher.Register(this);
}
public void Unsubscribe()
{
_newsPublisher.Unregister(_weakReference);
}
}
And now for concrete subscribers.
public class AlphaSubscriber : SubscriberBase
{
public Guid Id { get; set; }
public AlphaSubscriber(Guid id, INewsPublisher publisher):base(publisher)
{
Id = id;
}
public override void Update(string message)
{
Console.WriteLine($"{nameof(AlphaSubscriber)}-{Id} has recieved the message {message}");
}
}
public class BetaSubscriber : SubscriberBase
{
public int Id { get; set; }
public BetaSubscriber(int id, INewsPublisher publisher):base(publisher)
{
Id = id;
}
public override void Update(string message)
{
Console.WriteLine($"{nameof(BetaSubscriber)}-{Id} has recieved the message {message}");
}
}
Notice how the subscribers “Has-A” instance of subject. This allows it too unsubscribe themselves.
Let us now implement the abstract and concrete subjects.
public interface INewsPublisher
{
WeakReference<SubscriberBase> Register(SubscriberBase subscriber);
void Unregister(WeakReference<SubscriberBase> subscriber);
void Publish(string message);
void AddNews(string news);
}
public class NewsPublisherConcrete : INewsPublisher
{
public List<WeakReference<SubscriberBase>> _SubscribersCollection = new List<WeakReference<SubscriberBase>>();
public void AddNews(string news)
{
Publish(news);
}
public void Publish(string message)
{
foreach(var weakReference in _SubscribersCollection)
{
if(weakReference.TryGetTarget(out var subscriber))
{
subscriber.Update(message);
}
else
{
_SubscribersCollection.Remove(weakReference);
}
}
}
public WeakReference<SubscriberBase> Register(SubscriberBase subscriber)
{
var weakReference = new WeakReference<SubscriberBase>(subscriber);
_SubscribersCollection.Add(weakReference);
return weakReference;
}
public void Unregister(WeakReference<SubscriberBase> subscriber)
{
_SubscribersCollection.Remove(subscriber);
}
}
Notice how I have used weak references. This is a protection against memory leaks which may occur in the observer pattern. Every time the client adds a “news” item via the AddNews()
method, the Subject would notify the clients iterating over the the references which it has build and calling the Update
method of each interested(subscribed) observer. Let us now write client code to complete the example.
var publisher = new NewsPublisherConcrete();
var alphaSubscribers = Enumerable.Range(1,5).Select(x => new AlphaSubscriber(Guid.NewGuid(),publisher)).ToList();
var betaSubscribers = Enumerable.Range(1,3).Select(x => new BetaSubscriber(x,publisher)).ToList();
alphaSubscribers.ForEach(x => x.Subscribe());
betaSubscribers.ForEach(x => x.Subscribe());
Console.WriteLine("Adding message to News");
publisher.AddNews("Hello World");
alphaSubscribers.ForEach(x => x.Unsubscribe());
betaSubscribers.First(x => x.Id == 2).Unsubscribe();
Console.WriteLine("Adding another message to News");
publisher.AddNews("Another Hello World");
That’s all the Observer pattern is all about. In the next part, we will explore the EventAggregator
Pattern, a variant of Observer. The complete code samples could be found in my github.
2 thoughts on “Gof : Observer Pattern”