Design Patterns Simplified: Observer

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.

code map

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.

  1. interface ISubject  
  2.     {  
  3.         void Register(IObserver observer);  
  4.         void Unregister(IObserver observer);  
  5.         void Notify();  
  6.     }  
We also need to have Observer interface that will define the method to notify signals.
  1. public interface IObserver  
  2.     {  
  3.         void NotifyProductArrival();  
  4.     }  
Let’s have the concrete implementations now. We will start with Subject.
  1. public class ProductSubject : ISubject  
  2.     {  
  3.         private int _inventory;  
  4.         private List<IObserver> _observers = new List<IObserver>();  
  5.         public int Inventory  
  6.         {  
  7.             get { return _inventory; }  
  8.             set  
  9.             {  
  10.                 if (value > _inventory)  
  11.                 {  
  12.                     Notify();  
  13.                 }  
  14.                 _inventory = value;  
  15.             }  
  16.         }  
  17.   
  18.         public void Notify()  
  19.         {  
  20.             _observers.ForEach(p => p.NotifyProductArrival());  
  21.         }  
  22.   
  23.         public void Register(IObserver observer)  
  24.         {  
  25.             _observers.Add(observer);  
  26.         }  
  27.   
  28.         public void Unregister(IObserver observer)  
  29.         {  
  30.             _observers.Remove(observer);  
  31.         }  
  32.     }  
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.

  1. public class ProductObserver : IObserver  
  2.     {  
  3.         public string Name { getprivate set; }  
  4.         public ProductObserver(string name)  
  5.         {  
  6.             this.Name = name;  
  7.         }  
  8.   
  9.         public void NotifyProductArrival()  
  10.         {  
  11.             Console.WriteLine("Hey {0},New Product Inventory has been arrived"this.Name);  
  12.         }  
  13.     }  
At this point, we are done with the core classes. Now, let’s use these classes and see the output.
  1. Console.Title = "Observer pattern demo";  
  2.   
  3. ProductSubject subject = new ProductSubject();  
  4.   
  5. // ClientA takes a subscription to the product notification   
  6. IObserver observer1 = new ProductObserver("ClientA");  
  7. subject.Register(observer1);  
  8.   
  9. // ClientB also subscribes to the product notification  
  10. IObserver observer2 = new ProductObserver("ClientB");  
  11. subject.Register(observer2);  
  12.   
  13. //Increase in inventory happens and then all it's subscribers should be notified.  
  14. subject.Inventory+=1;  
Output

Output

Now, let’s unsubscribe one of the clients and see if message is going to him/her or not.
  1. //Now let's unsubscribe ClientA   
  2. subject.Unregister(observer1);  
  3.   
  4. //Increase in inventory happens but only ClientB will be notified as ClientA is unsubscribed.  
  5. subject.Inventory += 2;  
Output

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.


Similar Articles