Observer Pattern - Implementation In .Net Framework

This article describes the Observer pattern and the interfaces .Net Framework provides.

Observer Pattern

In this article, we will have a look at the observer pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state or data changes, usually by calling one of the method of observers (From Wikipedia).

In simpler words, the Observer pattern defines,

  • Subject (provider) that is responsible for monitoring and updating some sort of data and sending the notification to the observers
  • Observer that receives notification from Subject (provider).
  • Low level of loose coupling between Subject and Observers by defining Subject and Observers in such a way that none of them have any explicit knowledge of each other but Subject maintains a list of Observers.
  • Subject (provider) provides Observers a method to subscribe (attach) and unsubscribe (detach) themselves.
  •  A One-To-Many relationship between Subject (provider) and Observer which means a subject can have multiple dependents (observers) who want to get notified when change happens in the subject state, for example, a library named “TemperatureCalculatorLibrary” (Subject) which computes temperature can have many dependents applications (Observers) who need the updated temperature data for displaying or further calculation or predictions.
  • A solution for push-based notification where Subject provides Observers the updated data or notifies all the observers by calling one of their methods whenever there is any change happens to the subject’s state or data - there is no way that observers themselves update or get the data from a subject.

Now, let’s have a look at the general implementation to understand the Observer pattern in a more detailed way.

Define an interface named “IObserver” that contains a property to store a name of the observer object and differentiate two objects from each other. A method named “UpdateObserver” that is being called by the Subject to notify the observer object and this is the method which almost every observer class would have.

  1. public interface IObserver  
  2. {  
  3.     string ObserverName { getset; }  
  4.     void UpdateObserver();  
  5. }  
Let’s have a look at the implementation of "Subject" that maintains a list of "Observers". Since every Subject would have somewhat these methods, I tied those in an interface. The Subscribe-Unsubscribe method is the ones through which the Observers register-unregister themselves to the Subject. The “NotifyObservers” method is the one which has the responsibility of notifying all the Observers. We have added a property “SubjectState” to determine or hold the current state of the subject and the “ModifySubjectStateOrData” method is the one which is defined to modify the current state,
  1.     public class Subject   
  2.     {  
  3.         private readonly List<IObserver> _observers = new List<IObserver>();  
  4.   
  5.         public string SubjectName { getset; }  
  6.   
  7.         public Subject(string subjectName)  
  8.         {  
  9.             SubjectName = subjectName;  
  10.             SubjectState = "Created";  
  11.             Console.WriteLine("Instantiated named Subject {0}", SubjectName);  
  12.         }  
  13.   
  14.         public string SubjectState { getset; }  
  15.   
  16.         public void Subscribe(IObserver observer)  
  17.         {  
  18.             _observers.Add(observer);  
  19.             Console.WriteLine("Observer named {0} is now added to the {1}'s observers list and will receive notification when change happens to the subject {1} state", observer.ObserverName, SubjectName);  
  20.         }  
  21.   
  22.         public void Unsubscribe(IObserver observer)  
  23.         {  
  24.             _observers.Remove(observer);  
  25.             Console.WriteLine("Observer named {0} is removed from the {1}'s observers list and will no longer receive notification when change happens to the subject {1} state", observer.ObserverName, SubjectName);  
  26.         }  
  27.   
  28.         public void NotifyObservers()  
  29.         {  
  30.             foreach (IObserver observer in _observers)  
  31.             {  
  32.                 observer.UpdateObserver();  
  33.             }  
  34.         }  
  35.   
  36.         public void ModifySubjectStateOrData(string subjectStateOrData)  
  37.         {  
  38.             Console.WriteLine("{0} state changed from {1} to {2}", SubjectName, SubjectState, subjectStateOrData);  
  39.             SubjectState = subjectStateOrData;  
  40.             NotifyObservers();  
  41.         }   
  42. }  
Let’s have a look at the implementation of "Observer" that implements interface “IObserver”,
  1.  public class Observer : IObserver  
  2.   
  3. private Subject _subject;  
  4. public Observer(string observerName)  
  5. {  
  6.     ObserverName = observerName;  
  7. }  
  8.   
  9. public string ObserverName { getset; }  
  10.   
  11. public void Subscribe(Subject subject)  
  12. {  
  13.     _subject = subject;  
  14.     subject.Subscribe(this);  
  15. }  
  16.   
  17. public void Unsubscribe()  
  18. {  
  19.     _subject.Unsubscribe(this);  
  20. }  
  21.   
  22. public void UpdateObserver()  
  23. {  
  24.     Console.WriteLine("{0} received the notification", ObserverName);  
  25. }  
Let’s look at the main method which would complete our code for using the Observer pattern. 
  1. static void Main(string[] args)  
  2.     {  
  3.         Subject S1 = new Subject("S1");  
  4.         Observer O1 = new Observer("O1");  
  5.         Observer O2 = new Observer("O2");  
  6.   
  7.         O1.Subscribe(S1);  
  8.         O2.Subscribe(S1);  
  9.         S1.ModifySubjectStateOrData("Approved");  
  10.         Console.WriteLine();  
  11.   
  12.         O2.Unsubscribe();  
  13.         S1.ModifySubjectStateOrData("Implemented");  
  14.   
  15.         Console.ReadLine();  
  16.     }  
Within the Main method, we create an instance of Subject that we can subscribe/unsubscribe different observers. When the State in the Subject changes, we notify the Observers by their Update method (look at the code in Subject class).
 
Output


As we look at the above implementation we can see that Observer and Subject know about each other, which means to some extent they are coupled (not completely loosely coupled). To overcome this problem, .NET Framework provides a generic System.IObservable<T> and System.IObserver<T> interfaces which can be implemented by Subject and Observer to apply Observer pattern.

As mentioned earlier, the observer design pattern is suitable for distributed push-based notifications and requires

  • · A provider to send notifications to observers.
  • · An observer that receives notifications from a provider.

Implement a Provider using a generic System.IObservable<T> interface

Define a provider class that implements the System.IObservable<T> interface where T is the type of data that the provider sends to an observer.

  • The Provider class implements the single method “Subscribe” which provides an ability to an observer to register themselves with the Subject (provider). The “Subscribe” method stores a reference to the observer in a collection object, such as a List<T> object and returns a reference to an IDisposable interface that enables observers to unsubscribe (that is, to stop receiving notifications).

  • The provider class implements a method that is responsible for notifying the observers by calling any one of the Observer method IObserver<T>.OnNext, IObserver<T>.OnError, and IObserver<T>.OnCompleted implemented.

Implement an Observer using generic System.IObserver<T> interface

Define the observer class that implements the System.IObservable<T> interface where T is the type of data that observer received from the provider,

  • Defines a Subscribe method that calls the provider's Subscribe method and passes a reference of Observer instance
  • Define a private variable that will hold the IDisposable implementation returned by the provider's IObservable<T>.Subscribe method
  • Defines an Unsubscribe method that calls the Dispose method on the IDisposable object
  • Provide implementations of the three methods defined by the IObserver<T> interface: IObserver<T>.OnNext, IObserver<T>.OnError, and IObserver<T>.OnCompleted.

Points to Remember

  • The provider and observer may both try to remove the same member from the Observers list so both the Subscribe and Dispose of methods should be thread-safe.

  • We should avoid an attempt to unregister in the IObservable<T>.Subscribe method because it may result in a null reference. 

  • Although it is possible to attach an observer to multiple providers, the recommended pattern is to attach an IObserver<T> instance to only one IObservable<T> instance.

  • The observer should not throw exceptions from its interface implementations, such as OnNext or OnError. 
Now, let’s have a look at the real-world example to understand the Observer pattern .Net Framework implementation in a more detailed way,

Problem Statement

Suppose we have a video player which is responsible for playing video and keeping live video status, for example, how much  time has already elapsed, how much video time is remaining, what percentage of  the video is completed. Now, whenever video time elapsed changes by 10 minutes or video completed percentage exceeds  10 percent, all the observer GUI platforms must be notified so that respective GUIs can display current video live status.

The following example contains the complete source code for defining an IObservable<T> implementation for a Video Player monitoring application. It includes the VideoStatus structure, which is the data sent to observers, and the VideoStatusPercentageMonitor, VideoStatusMonitor, VideoStatusTimeMonitor - which are the IObserver<T> implementation.

Let’s have a look at the IObservable<T> implementation class named “VideoPlayer” – this class defines three methods to Start, Pause and Stop Video. A method named “GetVideoStatus” executing in a separate thread is initiated to update the current state in the data structure object i.e., VideoStatus,

  1.   public class VideoPlayer : IObservable<VideoStatus>  
  2.   {  
  3.       readonly List<IObserver<VideoStatus>> _observers;  
  4.       private VideoStatus _videoStatus;  
  5.       private readonly int _videoDuration = 0;  
  6.   
  7.       private Thread _thread = null;  
  8.       private readonly ManualResetEvent _mre = new ManualResetEvent(false);  
  9.   
  10.       public VideoPlayer(int videoDuration)  
  11.       {  
  12.           _observers = new List<IObserver<VideoStatus>>();  
  13.           _videoDuration = videoDuration;  
  14.       }  
  15.   
  16.       public void StartVideo()  
  17.       {  
  18.           _videoStatus = new VideoStatus()  
  19.           {  
  20.               TimeRemaining = _videoDuration,  
  21.               TimeElapsed = 0,  
  22.               VideoCompletedPercentage = 0,  
  23.               Status = VideoStatusCode.Running  
  24.           };  
  25.           _mre.Set();  
  26.           _thread = new Thread(GetVideoStatus);  
  27.           _thread.Start();  
  28.       }  
  29.   
  30.       public void PauseVideo()  
  31.       {  
  32.           _mre.Reset();  
  33.           _videoStatus.Status = VideoStatusCode.Paused;  
  34.           foreach (var observer in _observers)  
  35.           {  
  36.               observer.OnNext(_videoStatus);  
  37.           }  
  38.       }  
  39.   
  40.       public void StopVideo()  
  41.       {  
  42.           _thread.Abort();  
  43.           _thread = null;  
  44.   
  45.           _videoStatus.Status = VideoStatusCode.Stopped;  
  46.           _videoStatus.TimeRemaining = _videoDuration;  
  47.           _videoStatus.TimeElapsed = 0;  
  48.           _videoStatus.VideoCompletedPercentage = 0;  
  49.           foreach (var observer in _observers)  
  50.           {  
  51.               observer.OnNext(_videoStatus);  
  52.           }  
  53.   
  54.       }  
  55.   
  56.       public IDisposable Subscribe(IObserver<VideoStatus> observer)  
  57.       {  
  58.           lock (lockObject)  
  59.           {  
  60.               if (!_observers.Contains(observer))  
  61.                   _observers.Add(observer);  
  62.   
  63.               return new Unsubscriber(_observers, observer);  
  64.           }          
  65.   
  66.   
  67.       private class Unsubscriber : IDisposable  
  68.       {  
  69.           private readonly List<IObserver<VideoStatus>> _observers;  
  70.           private readonly IObserver<VideoStatus> _observer;  
  71.   
  72.           public Unsubscriber(List<IObserver<VideoStatus>> observers, IObserver<VideoStatus> observer)  
  73.           {  
  74.               this._observers = observers;  
  75.               this._observer = observer;  
  76.           }  
  77.   
  78.           public void Dispose()  
  79.           {  
  80.               lock (lockObject)  
  81.               {  
  82.                   if (_observer != null) _observers.Remove(_observer);  
  83.               }  
  84.           }          
  85. }  
  86.   
  87.       private void GetVideoStatus()  
  88.       {  
  89.   
  90.           for (var iCounter = 1; iCounter <= _videoDuration; iCounter++)  
  91.           {  
  92.               if (_thread == null || !_thread.IsAlive) continue;  
  93.   
  94.               _videoStatus.TimeElapsed++;  
  95.               _videoStatus.TimeRemaining--;  
  96.               _videoStatus.VideoCompletedPercentage =  
  97.                   Math.Round((double)(100 * iCounter) / _videoDuration, 2);  
  98.   
  99.               if (((_videoStatus.TimeElapsed % 60) != 0) &&  
  100.                   (!(_videoStatus.VideoCompletedPercentage > 0) ||  
  101.                    !(Math.Abs(_videoStatus.VideoCompletedPercentage % 10) <= 0))) continue;  
  102.               foreach (var observer in _observers)  
  103.               {  
  104.                   observer.OnNext(_videoStatus);  
  105.               }  
  106.   
  107.               _mre.WaitOne();  
  108.               Thread.Sleep(5000);  
  109.           }  
  110.   
  111.       }  
  112.   }  

Let’s have a look at the IObserver<T> implementation class named “VideoStatusPercentageMonitor”, “VideoStatusMonitor”, “VideoStatusTimeMonitor” – this implements the method IObserver<T>.OnNext and updates the status based on the data object VideoStatus received from the Publisher (provider).

  1. public class VideoStatusMonitor : IObserver<VideoStatus>  
  2. {  
  3.     private IDisposable _unsubscriber;  
  4.   
  5.     public virtual void Subscribe(IObservable<VideoStatus> provider)  
  6.     {  
  7.         _unsubscriber = provider.Subscribe(this);  
  8.     }  
  9.   
  10.     public virtual void Unsubscribe()  
  11.     {  
  12.         _unsubscriber.Dispose();  
  13.     }  
  14.   
  15.     public void OnCompleted()  
  16.     {  
  17.     }  
  18.   
  19.     public void OnError(Exception error)  
  20.     {  
  21.     }  
  22.   
  23.     public virtual void OnNext(VideoStatus value)  
  24.     {  
  25.         Console.WriteLine("------------------------------------------------");  
  26.         Console.WriteLine("Video is currently {0} ", value.Status.ToString());  
  27.     }  
  28. }  
  29.   
  30. public class VideoStatusTimeMonitor: IObserver<VideoStatus>  
  31. {  
  32.     private IDisposable _unsubscriber;  
  33.   
  34.     public virtual void Subscribe(IObservable<VideoStatus> provider)  
  35.     {  
  36.         _unsubscriber = provider.Subscribe(this);  
  37.     }  
  38.   
  39.     public virtual void Unsubscribe()  
  40.     {  
  41.         _unsubscriber.Dispose();  
  42.     }  
  43.   
  44.     public void OnCompleted()  
  45.     {  
  46.     }  
  47.   
  48.     public void OnError(Exception error)  
  49.     {  
  50.     }  
  51.   
  52.     public virtual void OnNext(VideoStatus value)  
  53.     {  
  54.         var ts = TimeSpan.FromSeconds(value.TimeElapsed);  
  55.         var format = $"H: {ts.Hours} M:{ts.Minutes} S:{ts.Seconds}";  
  56.   
  57.         var timeremaining = TimeSpan.FromSeconds(value.TimeRemaining);  
  58.         var timeremainingformat = $"H: {timeremaining.Hours} M:{timeremaining.Minutes} S:{timeremaining.Seconds}";  
  59.   
  60.         Console.WriteLine();  
  61.         Console.WriteLine("Video Player Time Monitor");  
  62.         Console.WriteLine("Time {0} elapsed : Remaining {1}", format, timeremainingformat);  
  63.     }  
  64. }  
  65.   
  66. public class VideoStatusPercentageMonitor : IObserver<VideoStatus>  
  67. {  
  68.     private IDisposable _unsubscriber;  
  69.   
  70.     public virtual void Subscribe(IObservable<VideoStatus> provider)  
  71.     {  
  72.         _unsubscriber = provider.Subscribe(this);  
  73.     }  
  74.   
  75.     public virtual void Unsubscribe()  
  76.     {  
  77.         _unsubscriber.Dispose();  
  78.     }  
  79.   
  80.     public void OnCompleted()  
  81.     {  
  82.     }  
  83.   
  84.     public void OnError(Exception error)  
  85.     {  
  86.     }  
  87.   
  88.     public virtual void OnNext(VideoStatus value)  
  89.     {  
  90.         Console.WriteLine();  
  91.         Console.WriteLine("Video Player Percentage Monitor");  
  92.         Console.WriteLine("Video {0} % completed ", value.VideoCompletedPercentage);  
  93.     }  
  94. }  
Let’s have a look at the Main method implementation which completes our observer pattern implementation – we have created an instance of VideoPlayer class and created an instance of three different VideoStatusMonitors; i.e., “VideoStatusPercentageMonitor”, “VideoStatusMonitor”, “VideoStatusTimeMonitor”. Finally, we attached these observers (monitors) with the VideoPlayer to get notified whenever Video Status changes. After all these initializations, we have called a method as below,

  • “StartVideo” to start video which also initiates a separate thread to update the current status of Video
  • Wait for around 2 seconds then call method “PauseVideo” to pause the video
  • Wait for around 2 seconds then call method “StartVideo” to re-start the video
  • Wait for around 3 seconds then call method “StopVideo” to finally stop the video

Please note that during this process VideoStatusMonitor will keep displaying the current i.e., “Running”, “Paused”, or “Stopped” state, VideoStatusTimeMonitor will keep displaying Video Time Elapsed and Video Time Remaining and VideoStatusPercentageMonitor will keep displaying the Video completed in percentage based on the data received from the provider (IObservable implementation).

  1. static void Main(string[] args)  
  2. {  
  3.     VideoPlayer videoPlayer = new VideoPlayer(360);  
  4.   
  5.     VideoStatusMonitor videoStatusMonitor = new VideoStatusMonitor();  
  6.     videoStatusMonitor.Subscribe(videoPlayer);  
  7.   
  8.     VideoStatusTimeMonitor videoStatusTimeMonitor = new VideoStatusTimeMonitor();  
  9.     videoStatusTimeMonitor.Subscribe(videoPlayer);  
  10.   
  11.     VideoStatusPercentageMonitor videoStatusPercentageMonitor = new VideoStatusPercentageMonitor();  
  12.     videoStatusPercentageMonitor.Subscribe(videoPlayer);  
  13.   
  14.     videoPlayer.StartVideo();  
  15.     Thread.Sleep(2000);  
  16.     videoPlayer.PauseVideo();  
  17.     Thread.Sleep(2000);  
  18.     videoPlayer.StartVideo();  
  19.     Thread.Sleep(3000);  
  20.     videoPlayer.StopVideo();  
  21.   
  22.     Console.ReadLine();  
  23. }  

Output

 
 
Summary
  • The Observer design pattern defines an observer, which registers for notifications, and a subject (provider), which monitors data and sends notifications to one or more observers.

  • The Observer design pattern is suitable for distributed push-based notifications.

  • The Observer pattern is mostly implemented in a synchronous way, i.e. the Subject calls the appropriate method of all its observers when some event occurs.

  • The Observer Pattern is the foundation of the Model View Controller (MVC) pattern, in which a view is updated automatically whenever the model's state changes.

  • The Observer Pattern a one-to-many relationship between two objects that allows one or many observed objects to be updated by the subject object.