One pattern that has always been interesting to me is the Observer Pattern. It's incredibly useful and the authors (of the Gang of Four book) were obviously ingenious, yet I always found the implementation of the pattern somewhat clunky.
I then have to remind myself that the book was written in the early 90s and the OO technologies available then weren't nearly as flexible as they are today. Below is an Observer Pattern UML class diagram. This is a screenshot taken directly from dofactory.com:
http://www.dofactory.com/Patterns/PatternObserver.aspx#UML |
Back to the pattern as shown above. The idea is fairly simple:
- There is a Subject, which is the object being observed.
- The subject maintains a list of Observers, which are objects that are interested in the subject and want to know when its state changes.
- In order to subscribe as an observer, an object must be supplied to the subject's .Attach(observer) method. Then, when .Notify() is called on the subject, the subject loops through all its registered observers and calls .Update() on each observer. This lets each observer know that the subject's state has changed.
Brilliant!
However, there are two things I don't like about this straight out of the gate. I have these listed as "Problems" below. I want to make clear that the pattern works just fine like it is listed above, so these aren't technically problems. They're just things I'm complaining about and then suggesting a solution for, in order to enable a better implementation of the pattern's original premise, which is to allow one or more objects to observe another object.
- Problem - I question the idea that the observer objects should maintain a reference to the subject. After all, the subject is already maintaining a list of all the observers.
- In fact, in the GoF book, the authors mention that this pattern can be implemented multiple ways and that an alternate implementation could omit this (in my opinion unnecessary) reference and supply the subject as a parameter in the observer's Update method. I like this idea much better.
- Solution - This is an easy one to fix - just make the change listed above and supply the subject as a parameter in the Update method, like so - .Update(subject).
- Problem - I also dislike the fact that, in the UML diagram, each ConcreteObserver must be an Observer. In terms of C#, that means that each concrete observer must either inherit from an observer base class or implement an observer interface.
- If I had to pick one of the two, I would definitely go the interface route and have each observer implement an interface - probably a generic interface - something like IObserver<TSubject>. However, fortunately C# provides, in my opinion, a better way to do this that involves complete decoupling of the subject and observer, which is desirable.
- Solution - C# delegates come to the rescue here, in that they allow us to encapsulate a method call in a delegate instance and register that in place of an object. In other words, since the subject maintains a list of its observers, what I'm saying is that the subject can simply maintain a list of methods that it must invoke instead of the actual observer objects, themselves. Since every single Update method will have the same signature, this is 100% feasible. The signature would be .Update(Subject s), which could be stored in a generic list, like so: List<Action<Subject>>.
I'm kind of rushing through this without providing as much detail as possible. Perhaps a concise code sample will communicate my approach more thoroughly. The sample below is purposefully trivial in nature and is only meant to get the idea across.
The approach outlined in this post has probably been rehashed a million times by various developers, but this is my two cents. I say, if you can achieve the same result by leaving all types involved almost completely decoupled, why not?
The approach outlined in this post has probably been rehashed a million times by various developers, but this is my two cents. I say, if you can achieve the same result by leaving all types involved almost completely decoupled, why not?
No comments:
Post a Comment