Overblown Delegates

Alexey Kuznetsov
3 min readDec 19, 2016

--

Over a year ago I stopped using NSNotifications and KVO in favor of a plain old observer pattern. Here’s what I have to say after using it for a year.

Not going back

I never regretted making this decision during this year. My APIs became more obvious and testable. I forgot about the huge decoupling of NSNotifications. I never suffer from the inconvenient KVO API and crashes because I forgot to remove a KVO observer.

Naming and roles

One can say that using the observer pattern is the same as a wide-spread delegation in iOS and Mac development world. I’d say, it’s the same from the implementation point of view, but different on the higher level.

The problem with the word delegate is that the role is not very specific. Just by looking at a type called StoreDelegate it is not immediately clear what to expect from it. Is it supposed to handle Store events? Is it supposed to provide additional information for the Store?

The fact is, Cocoa and Cocoa Touch APIs tend to have overblown delegates. Just look at the UITableViewDelegate! It configures rows, handles selection events, reorders rows, manages table view focus and much more.

Interface segregation principle

The UITableViewDelegate API promotes the violation of the Interface Segregation Principle. The principle suggests that no client should be forced to depend on methods it does not use. The interfaces should be split into smaller and more specific ones. It makes the whole system more decoupled and thus easier to refactor, change, and test.

In a project, you come across some UITableViewDelegate implementation and ask yourself, what exactly it does. And you don’t know! It may do anything from what that huge protocol declares.

The first step in solving this problem could be to split that big protocol into the smaller ones. There could be:

UITableViewRowConfigurationDelegate
UITableViewAccessoryViewConfigurationDelegate
UITableViewHeaderFooterConfigurationDelegate
UITableViewSelectionDelegate
UITableViewDisplayDelegate
UITableViewRowEditingDelegate

and so forth.

You might argue that it’s too many types. I would say it reveals the truth instead of hiding it from us. It shows the real situation in which a table view may depend on objects with so many different roles.

In fact, if we were the designers of the UITableView, that would be a signal for us to decompose the UITableView itself into several objects, each of which having a distinct role and a single delegate.

Event targets

I personally still don’t like that every decomposed protocol is a delegate. (Or if everything was an observer as I started to name them a year ago.) One of the typical roles of Cocoa and Cocoa Touch delegates is event handling. Such types can be called event targets. This is how the list of decomposed protocols looks now:

UITableViewRowDelegate
UITableViewAccessoryViewDelegate
UITableViewHeaderFooterDelegate
UITableViewSelectionEventTarget
UITableViewDisplayEventTarget
UITableViewRowEditingEventTarget

The word delegate is kept only for the configuration roles (by the way, do you have a better name?). And the expression event target is only used for the event handling role.

Now the implementations responsible for configuration can be separated from those responsible for events.

Event sources

Imagine you wanted to react when the list of audio devices in the system changes. You make your use case implement, say, an AudioDevicesChangeEventTarget protocol. But which name do you give to a type that produces those events? The system libraries may just provide a low-level C API with function callbacks. You build a wrapper and name it what? A DevicesChangeNotifier? Could be, but I like how well an EventSource plays with an EventTarget:

AudioDevicesChangeEventSource
AudioDevicesChangeEventTarget

Conclusion

We can’t change the UIKit API, but we could design our own APIs better. The overblown delegate protocols promote the violation of the Interface Segregation Principle making our systems more coupled and harder to refactor, change, and test. Making smaller delegate protocols and picking more specific names for them improves the app architecture.

Related Articles

--

--