In Defense of Observers
Recently Brent Simmons posted his thoughts about large cocoa projects, which elicited a lot of responses within the Mac programmer community about what makes code easier to “research.” There’s a lot of good stuff there, but I wanted to focus on one facet of the discussion that was brought up, namely NSNotificationCenter.
First, anyone who has read the Design Patterns book knows that NSNotificationCenter is the observer pattern. It is useful for decoupling objects, in which is a good thing, because it makes classes more reusable (because they have fewer dependencies) and more maintainable, because, if done correctly, it insulates changes in one class from another class. These are all good things, and observers are good, and you should use them.
The problem comes up with Apple’s implementation of NSNotificationCenter, and I’ve often wondered why they chose the implementation they did. There are a few problems with it:
- As noted by Michael Tsai, NSNotificationCenter is a singleton, and thus essentially is one big honkin’ global. Globals are bad ™.
-
NSNotificationCenter allows a many-to-many relationship between observers and their subjects. That is, many different subjects can send the same notification, and several observers will listen for the notification, regardless of the sender.
This breaks the observer pattern, which was intended as a one-to-many relationship, because it breaks the idea that the observer observes the subject. Instead it produces the idea that the observer observes the notification, which is wrong. The notification is simply a convenient way to pass information between classes, it is not really a part of the relationship between the subject and the observer.
- The syntax of using NSNotificationCenter puts the notification center as the receiving object, and thus puts the focus on the notification center, instead of focusing on the relationship between the subject and its observer.
These problems are compounded by the fact that just about all sample code that I’ve seen, from Apple and others, use NSNotificationCenter in the many-to-many relationship, instead of a one-to-many relationship.
Of course Cocoa uses another design pattern that can be used as a stop-gap for the observer pattern: delegates. Delegates define a simple one-to-one relationship between the subject and its delegate. It has the benefit of being easy to implement and clarifying the relationship between the subject and delegate. The problem is, it’s not appropriate for the observer pattern, for a couple of reasons:
- As the name implies, a delegate is delegated tasks or decisions. That is, a subject is dependent on the decisions a delegate makes, and will change behavior depending on if, and which, delegate is attached. This is the opposite of an observer pattern. The subject does not care who is listening, or if anyone is listening at all. The observer, however, will often change behavior or take action, depending on what the subject does. In other words, the dependency is modeled in the exact opposite direction in the delegate pattern.
- The delegate pattern is a one-to-one relationship, but the observer pattern is a one-to-many. It makes sense that a delegate subject would only have one delegate making decisions for it, but it doesn’t make sense that an observer subject can only have one observer listening to it.
Still, you can trivially implement a one-to-one version of the observer pattern using the same technique as implementing a delegate. You just shouldn’t call it a delegate because you’re implying the wrong direction for dependence.
All this said, there’s a much better way to implement the observer pattern, which I’ve seen in just about every other framework I’ve used. Things get a little tricky in Objective-C because the lack of multiple inheritance disallows use of mix in classes. However, it can still be done. Essentially you’d need two classes: ObserverSubject and Observer.
ObserverSubject maintains a list of Observer objects that are listening to the subject. It has hidden methods, called only by Observer, for connecting and disconnecting interested Observers, and a notify method for the actual subject to notify all listeners that an interesting event has happened.
The Observer class maintains a list of all ObserverSubjects that it is currently listening to. This is for the express purpose of automatically disconnecting from them when the Observer object is destroyed. The main method on the Observer class is called watch, and connects the Observer with the Subject. The Observer class also keeps a pointer to the actual observer, which it forwards all notifications to.
A simplified example of how these classes would be used:
@interface MySubject {
}
- (ObserverSubject*) subject;
@end
@interface MyObserver {
}
- (Observer*) observer;
@end
MySubject* mySubject = [[MySubject alloc] init];
MyObserver* myObserver = [[MyObserver alloc] init];
[[myObserver observer] watch:[mySubject subject]];
In this design, the one-to-many relationship is enforced, and the syntax puts the focus on the relationship between the subject and its observer. Also note the lack of globals.
In conclusion, observers are a good thing, and you shouldn’t write them off just because Apple did a shoddy job of implementing them. Modeling simple one-to-one observers is trivial to implement, and modeling a true one-to-many observer pattern isn’t all that difficult either.