13

Sometimes I come across these message-hub-style APIs, for example the Cocoa NSNotificationCenter: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html

Usually these APIs provide a global access point on which you subscribe to or broadcast messages/events. I'm thinking this is a problem because it encourages a flat and unstructured program architecture, where dependencies are not explicit in the API, but hidden in the source code. You are not forced to think about object ownership and hierarchies, but can rather make any object in your program result in any code anywhere being called. But maybe this is a good thing?

Does this pattern generally encourage good or bad program design, and why so? Does it make the code harder or easier to test?

Forgive me if this question is too vague or broad. I'm trying to wrap my head around the potential consequences of extensive use of an API like this, and the different ways you could use it.

Edit: I guess my biggest issue with this pattern is that the API "lies" about dependencies and object couplings, and can be illustrated with this example:

myObj = new Foo();
myOtherObj = new Bar();
print myOtherObj.someValue; // prints 0
myObj.doSomething();
print myOtherObj.someValue; // prints 1, unexpectedly, because I never indicated that these objects had anything to do with each other
Magnus Wolffelt
  • 2,373
  • 2
  • 20
  • 23
  • Are you question this example specifically or the listener pattern in general? – TheLQ Nov 30 '10 at 12:27
  • I believe this is broader than the listener pattern. The listener pattern can be implemented "cleanly" with a well-defined object structure and registering listeners on specific objects. My uncertainty is about the global message/event hub pattern. – Magnus Wolffelt Nov 30 '10 at 12:37

4 Answers4

6

Asynchronous messaging is a good architectural principal for large systems that must scale

The Java equivalent of this is JMS and generally considered to be a good thing.

This is because it promotes decoupling of your client code from the code that actually services the message. Client code merely has to know where to post their message. Service code merely has to know where to pick up messages. Client and service know nothing of each other and therefore can change independently of each other as required.

You can easily externalise the URI of the message hub to make it configurable and not embedded in the source code.

Gary
  • 24,420
  • 9
  • 63
  • 108
6

I wouldn't go as far as saying it encourages bad programming. But it can easily be misused.

Well what is the actual idea?
The source of the notification only makes its notification. It doesn't make an assumption about the existence of potential observers or anything. An observer registers for notifications it is designed to handle. The observer doesn't make any assumptions about how many potential sources there are for the notifications it may handle.

This is a way to achieve dependency injection, without ever having sources know observers or observers know sources. However for the whole system to work, you need to wire up the right observers for the right notifications, and this system is even vulnerable to typos, because it cannot be compile time checked.

The biggest danger of course is, that someone will use this to make tons of objects globally available for 1-1 calls.

back2dos
  • 29,980
  • 3
  • 73
  • 114
4

This is a typical Oberserver Pattern implementation (or sometimes called a Listener pattern or sometimes called Subscriber/Publisher pattern). In applications where this is behavior useful it's a good pattern to implement. No pattern should be implemented if it adds no value to the solution.

From your question, it sounds as if you are concerned that everything knows about the NotificationCenter and that global things are BAD. Sometimes, yes, global things are bad. But let's look at the alternative. Let's say you have 2 components, for this example it doesn't really matter what they do or what they are. Component1 has some data that was acted upon or changed in some way. Component2 would like to know about any changes to data of the type that Compoent1 manages. Should Component2 know about the existence of Component1? Or would it be better for Component2 subscribe/listen for a message that tells it that some component somewhere in the app changed some data that it is interested in? Now take this example and multiply it by dozens or more components and you can see where the value of the pattern lies.

Is it a perfect solution for every situation, no. Does it abstract communication in between components and provide a more loose coupling, yes.

Walter
  • 16,158
  • 8
  • 58
  • 95
  • 1
    Reading on wikipedia, the observer pattern definition does not appear to include a globally available event hub. If the event hub was passed into the constructor/method of all objects concerned, I would consider this a good pattern. It is indeed the global access point and its state that makes me uncertain. – Magnus Wolffelt Nov 30 '10 at 15:54
0

It's good for event-driven systems, and is nicer than the alternative of having a bunch of Observers observing each other, as you're less likely to wind up with inadvertent infinite loops of observers firing events. This was a real problem in the old VB days, when you had event-driven ActiveX controls that could be wired up to each other with event handlers.

TMN
  • 11,313
  • 1
  • 21
  • 31