I have been reading about data binding in windows phone 7 development and got curious about how .net implements the observer pattern. I spent some time understanding the flow and decided to write a blog post about it.
.NET platform offers IObserver
Compare this scenario to the day before Eid Ul Fitr. Tomorrow is Eid if we see the moon. Now, you have decided to go to the roof and keep waiting there until the moon pops up. Knowing this, some of your friends (who are so computer freak that they don’t even want to leave their PCs to watch the moon of Eid Ul Fitr) called you and asked you to notify them if you see the moon.
Here:
The Moon == Subject
You == Observable
Your nerd friends == Observers
Here the observable keeps watching for the subject, when the subject pops up in the sky, the observable calls the observers – that’s it, simply that’s the observer pattern.
To implement this pattern, we define a Subject object. It has a counter. Every time you view the counter, it gets increased by one. We are doing this little magic to track any changes in the subject.
Then we define the SubjectObservable object which implements the IObservable. It accepts a subject in it’s constructor. This is not mandatory but we did this to track the subject in a better fashion. It provides a Subscribe() method which is called by the observers to subscribe to the observers. The method must return an IDisposable object. We do a trick and create a custom object implementing the IDisposable interface. In the constructor we accept the list of observers recorded by the observable and the instance of the observer passed for subscription. In the Dispose() method we remove the observer from the observer list. So when the Dispose() method of the disposer is called, the observer is removed from the observer list. The observable will no longer send any notifications to the removed observer.
The SubjectObserver is pretty simple. It has OnNext(), OnCompleted() and OnError() methods. The OnNext() is called by the observable to send the current state of the subject. Besides the abstract methods, we also define a constructor to accept a unique name instance and an instance of an observable to follow. When the OnNext() is called, we print out the current counter value of the subject. We also define a Dispose() method which shall invoke the Dispose() method of the disposer, removing itself from the observer list of the observable.
Well, if all these theories seem too complex to you. Do read the codes below. I have tried to add relevant comments. If you have any feedback, please do make a comment.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
using System; using System.Collections.Generic; using System.Linq; namespace Test { class Program { public static void Main(string[] args) { // Create a subject and the observable, inject the subject into the observable Subject subject = new Subject(); SubjectObservable observable = new SubjectObservable(subject); //Create two observers SubjectObserver zeus = new SubjectObserver(observable, "Zeus"); SubjectObserver ares = new SubjectObserver(observable, "Ares"); // Run a loop and trigger the observer for (int i = 0; i < 5; i++) { Console.WriteLine("Counter: " + subject.Counter); observable.inspectSubject(); } Console.ReadLine(); } } // // Class Subject - It has a counter object that increases everytime you inspect it // class Subject { private int _counter; public int Counter { get { return ++_counter; } } } // // Class Subject Observable - The observable object that observes the subject and notifies the observers // class SubjectObservable : IObservable<Subject> { // List of observers and the instance of subject private List<IObserver<Subject>> _subjectObservers; private Subject _subject; // Accept a subject instance which we shall observe public SubjectObservable(Subject subject) { _subjectObservers = new List<IObserver<Subject>>(); _subject = subject; } // This method allows the observers to attach themselves. It returns a disposer object to the observer // which the observer can utilize to unsubscribe public IDisposable Subscribe(IObserver<Subject> observer) { if (!_subjectObservers.Contains(observer)) { _subjectObservers.Add(observer); } return new Disposer(_subjectObservers, observer); } // This method is used to inspect the subject over time. Usually used with a timer or an event public void inspectSubject() { foreach (IObserver<Subject> observer in _subjectObservers) { observer.OnNext(_subject); } } // // Private class Disposer: Implements the IDisposable. Observable returns an instance to the observer for easy unsubscription // private class Disposer : IDisposable { // The observers list recieved from the observable private List<IObserver<Subject>> _subjectObservers; // The observer instance to unsubscribe private IObserver<Subject> _observer; public Disposer(List<IObserver<Subject>> _subObservers, IObserver<Subject> observer) { _subjectObservers = _subObservers; _observer = observer; } public void Dispose() { if (_subjectObservers.Contains(_observer)) { _subjectObservers.Remove(_observer); } } } } // // Class Subject Observer - The object that attaches itself to a observable and recieves notifications // class SubjectObserver : IObserver<Subject> { // The disposer returned by the observable when subscribing private IDisposable _disposer; // Unique instance name to track the observer private string _instaceName; public SubjectObserver(IObservable<Subject> provider, string instanceName) { if (provider != null) { _disposer = provider.Subscribe(this); _instaceName = instanceName; } } // The observable invokes this method to pass the Subject object to the observer public void OnNext(Subject value) { Console.WriteLine("[" + _instaceName + "] " + value.Counter); } // Usually called when a transmission is complete. Not implemented. public void OnCompleted() { throw new NotImplementedException(); } // Usually called when there was an error. Didn't implement. public void OnError(Exception error) { throw new NotImplementedException(); } // Invoke the disposer recieved from the observable. This will unsubscribe public void Dispose() { _disposer.Dispose(); } } } |
Okay, so every time we read the value of the counter data, the value increases. The subject is inspected by the observable and the observable passes the subject to the observers. Each observer again prints the counter and increases the value by one. This continues. 🙂