Observer pattern is a powerful software design pattern that allows observers to subscribe for event updates from a subject.
Following the Hollywood Principle, it offer for low-coupling and high cohesion between the subject and its observers.
Everyone talks about the good thing, but what about its pitfalls?
In observer pattern, it is typical to have multiple observers observing a subject.
Here is a simple observer pattern setup.
Among the observers for a subject, the observers should be treated equally. This is very important and here is why.
The moment a subject provides preferential treatment for a particular observer, it becomes highly-coupled with the observer. And this defeats the purpose of the observer pattern.
By preferential treatment, it could be several things.
- Order of notification (e.g. Observer A must be notified before Observer B and C)
- Conditional notification (e.g. Observer A only likes event X and Observer B only likes event Y).
- Specialized notification (e.g. Create a event specialized just for Observer A)
In my experience, observer pattern usually starts out with a good implementation. But as events and interactions among objects become more complex, the assumption that observer equality becomes increasingly difficult to uphold. It is very tempting to just “hack” a few lines to get something work. Unfortunately, complexity will keep increasing, and a few lines of hack morph into nasty spaghetti code.
Cycle In Event Flow
Within observer pattern, another pitfall to watch out is the cycles in the event flow.
With a cycle in the event flow, it will likely cause undesired recursion behavior. In worst case, the cycle involves the subject, and may cause an infinite recursion.
This may seem obvious at a glance. After all, who would purposely design a cycle within an observer pattern. But remember, software has no shape or form. It is easy to introduce unintentional cycles in complex architecture that you are unfamiliar with.
Observer pattern has been somewhat standardized into libraries in modern programming languages.
Java Beans has a powerful utility class called PropertyChangeSupport that standardize event notification and listener interfaces. Ruby has observer.rb that is similar to Java, and it is simple and powerful. I have good experience with both of them.
And as usual, C++ has an overengineered solution called Boost Signal and Slots. The difference here is that it is tailored toward functional programming. Notification callbacks are done in forms of boost function pointers (object wrapper on actual function pointers). I dislike it because the observer pattern is hidden from the class interface, and it creates call stack that makes your eyes bleed. The original design of Signal and Slots is so bad that they have to make a Signal and Slots 2 library. 😆