Recently, Guy English posted an an opinion piece about the pitfalls of hooking up your Cocoa controls’ actions to First Responder. Then, Brent Simmons followed up on that, including some other thoughts, and settling, in the end, on using a delegate pattern. I thought that, to some extent, they might be missing the forest for the trees…
Guy indicated that he feels like the decoupling offered by the responder chain is too great – that sending an arbitrary message up the chain, with only the sender for context, is insufficient to convey user intent. I can see that criticism, and Brent already covered one alternative option for more explicit, delegate-based patterns.
The thing that this responder chain discussion has been missing, in my opinion, is the power of the polymorphism offered by the pattern. Put differently, maybe we should consider where this pattern really shines before eschewing it. I readily admit that it might “shine” more frequently on OS X than iOS, but the principles are the same for both.
The most obvious example is event handling: Mouse click (or scroll event, or whatever) has happened… who wants it!? If the most specific subview doesn’t, it makes sense to try the next view up, etc. It’s great for this, but this is a trivial example.
It can also be great for higher-level application user actions too. For instance, take common UI idioms like
-paste:. Maybe it makes less sense that an individual view would handle those, but when the message gets to an
NSViewController, it starts to become evident that the “context” will be exogenous to the action message itself and, in the first most obvious case, needs to come from the responder receiving the event. From an
NSViewController, the message might go on to an
NSWindowController, then to an
NSDocument instance (and so on). Surely one of these controller has the context (in this example, maybe selection state) to know how to
The pattern is also powerful in this regard because when implementing a responder, you always have the option of making a run-time decision on whether that responder can/will handle the message, or whether it should be ant on up the chain (whereas the delegate-based pattern effectively makes this a build-time decision). There are a bunch of different ways to do this. You could override
-respondsToSelector: to make a decision at runtime as to whether you can handle the action. There are other cut-points on the responder chain, too. You can fork/detour the responder chain altogether by implementing
-supplementalTargetForAction:sender:. You can also just accept the action, do something, and then re-send the action on up the chain to the next responder.
The other thing that bugs me about the delegate-based approach to the tableCell example addressed by Guy and Brent, is that it creates a bunch of explicit coupling where it’s not needed. Consider this alternative: instead of making an explicit delegate connection, intercept the action on the responder chain at the level where the “context” exists, replace the sender with something that vends the necessary context (possibly by convention (i.e.
-representedObject, or by having the replacement sender implement an explicit protocol), and then send the action to your
-nextResponder, letting it percolate on up the chain to its eventual handler.
The delegate-based approach also “cuts off” the chain early, and the argument seems to be that it cuts it off at the “right” level: where context first appears. But what if there’s more than one “right” level, or more than one scope of context? Imagine a nested selection model where you have a table view with a text view in each row. If the text view is editing and has a text selection, then you probably want to implement
-copy: by copying just the text. However, if the text view has no selection (i.e. just an insertion point), then you might want to implement
-copy: by copying the entire row of the table. If you always cut off the responder chain at the first point where context exists, you could be missing a lot of opportunity.
In the specific case of the button in a table cell, the view controller (which would be in the responder chain) could implement the action method, and then replace the sender with some other context bestowing object, before forwarding the action to its -nextResponder. This has the advantage of retaining the polymorphism and dynamism of the responder chain while addressing the ‘lack of context’ problem of an action message with an arbitrary button as sender. If your view controller lacks the appropriate context, the answer might be “more-granular view controllers”. For a concrete example of this, see Corbin Dunn’s blog posting about a view-controller-per-row NSTableView.
The responder chain isn’t the right tool for every job, but it’s pretty darn good at what it does, and can be applied to many situations to great effect, with just a little extra thought. The proposed delegate-based solution is more explicit, but also more tightly coupled, and in my opinion, somewhat more brittle.
UPDATE: Guy has responded, and apparently I’ve mistaken intentional silliness for dramatic emphasis; My bad. I read both Guy’s and Brent’s blogs regularly. I think they’re both smart guys. It was never my intent to misstate anyone’s position. Anyway, it’s been fun to do some quasi-collaborative chin-stroking on one of the central patterns many of us work with every day.