I am here to discuss one of the popular behavioral design patterns called Observer. Before talking about its implementation, let’s begin with defining it.
As per the GOF guys, Observer pattern is defined as the following:
“Define a one-to-many dependency between objects, so that when one object changes state, all its dependents are notified and updated automatically.”
Well! Let’s understand what they mean and where this pattern can fit. Observer pattern is used in cases when any signal/event needs to be notified to multiple observers or clients. The condition of sending signals can be customized based on the business need.
Observer pattern in .NET Framework
Delegate, in .NET, by default supports the observer pattern, especially multicast delegate, that can be attached to any event or user action to call registered event handlers.
Generic interfaces IObserver and IObservable can also be used to implement the observer pattern as they provide required methods, by default. If you are interested in knowing how to work with them, you may refer to this link.
In WPF and Windows Forms, you might have implemented INotifyPropertyChanged that also supports Observer pattern. OK! So, we discussed about out of the box features that directly or indirectly support observer pattern. Now, let’s talk about the custom implementation of the pattern. Let’s have a look at the code map diagram from the example we will be going through in the rest of the article.
Observer pattern has some key components, as following.
- Subject -> Provides interface to attach/detach observer objects. In the diagram above, ISubject does this job.
- ConcreteSubject -> Provides the implementation of subject interface and handles notification logic. Here, ProductSubject has been used for this.
- Observer -> Defines the method to handle notification. In our example, IObserver is designed for this.
- ConcreateObserver -> Provides implementation to the notification method. We have ProductObserver doing this job.
How Observer pattern works
Let’s understand this by a simple example.
We will first create Subject interface as following.
- interface ISubject
- {
- void Register(IObserver observer);
- void Unregister(IObserver observer);
- void Notify();
- }
We also need to have Observer interface that will define the method to notify signals.
- public interface IObserver
- {
- void NotifyProductArrival();
- }
Let’s have the concrete implementations now. We will start with Subject.
- public class ProductSubject : ISubject
- {
- private int _inventory;
- private List<IObserver> _observers = new List<IObserver>();
- public int Inventory
- {
- get { return _inventory; }
- set
- {
- if (value > _inventory)
- {
- Notify();
- }
- _inventory = value;
- }
- }
-
- public void Notify()
- {
- _observers.ForEach(p => p.NotifyProductArrival());
- }
-
- public void Register(IObserver observer)
- {
- _observers.Add(observer);
- }
-
- public void Unregister(IObserver observer)
- {
- _observers.Remove(observer);
- }
- }
The class designed above (ProductSubject) does the following things:
- Implements member of ISubject interface.
- Handles the notification logic in property Inventory.
- Uses IObserver collection to register/unregister the subscription.
The time has come to have a final concrete class for IObserver.
- public class ProductObserver : IObserver
- {
- public string Name { get; private set; }
- public ProductObserver(string name)
- {
- this.Name = name;
- }
-
- public void NotifyProductArrival()
- {
- Console.WriteLine("Hey {0},New Product Inventory has been arrived", this.Name);
- }
- }
At this point, we are done with the core classes. Now, let’s use these classes and see the output.
- Console.Title = "Observer pattern demo";
-
- ProductSubject subject = new ProductSubject();
-
-
- IObserver observer1 = new ProductObserver("ClientA");
- subject.Register(observer1);
-
-
- IObserver observer2 = new ProductObserver("ClientB");
- subject.Register(observer2);
-
-
- subject.Inventory+=1;
Output
Now, let’s unsubscribe one of the clients and see if message is going to him/her or not.
-
- subject.Unregister(observer1);
-
-
- subject.Inventory += 2;
Output
As you can see in the above output, only ClientB has received the message because ClientA has been unsubscribed.
Conclusion
In the article, we have gone through what Observer pattern is and when and how to use it. Please note that it’s one of the most commonly used behavior patterns and this is one of the reasons of having inbuilt .NET interfaces to support the pattern.
You can also download the attached demo project (ObserverPatternDemo.zip) to go through the full source code, referred in the article.
Hope you have liked the article. Look forward for your comments/suggestions.