Delegation does not reusable code make

Korey Hinton —> Blog —> Delegation does not reusable code make

Are you serious?

Before you think this is just an article where I'm venting about the delegation pattern being misused or abused, I'm not. What I'm saying is that it can't be reused! While I'm not good at proofs, here is my attempt anyway to prove to you that delegation code does not follow the principles of good code reuse. ../media/yoda.jpg

Does the delegation pattern promote loose-coupling or tight-coupling?

I'm not sure of the source, but I've heard before that delegation promotes loosely-coupled code. This is actually just a half-truth. There are two ends of the stick: the delegator and the delegate. The delegator is loosely-coupled to its delegate. It doesn't care what kind of object it is, it can just assume it conforms to the delegate protocol and can send the delegate messages directly from that protocol. Usually the delegator isn't even the one that is responsible for assigning the delegate object. He can remain completely stupid about the delegate and doesn't have to know any of its inner workings.

You can see how coupled something is based on the number of places you have to reference the other class or object in your implementation file. So what are the steps for the delegate? He gets the short end of the stick and sadly this is the reusable part. Anybody who wants to conform to the delegate protocol has to follow these steps:

  1. Import header file of protocol/delegator
  2. Add the declaration that you conform to that protocol
  3. Obtain a pointer reference to the delegator object
  4. Set yourself as the delegate to the delegator
  5. Implement the required methods declared in the protocol

The first 4 steps are a pain already, but number 5 is where you really get things tightly coupled. If I am going to respond to these messages sent from the delegator, then this in and of itself implies that I need to know its inner workings without seeing what its part is doing.

Ok so if you're still not convinced that this is a pain, think about the first time you had set up a delegate and datasource to UITableView. I don't know about you but it seemed like a lot of work. Ok so now that we know the process are these steps too hard to be used in a reusable way? I say yes. If your boss came to you and said we need to build an app that has 50+ table views, how excited would you be to set-up all those delegates and datasources?

Best case reuse example

So let's give delegation the benefit of the doubt. Let's say a third-party has created a framework, which would be the most reusable way to share their code with you, and they expose a public interface allowing you to be a delegate to some work they are going to do for you. Now without knowing anything about your code they've set-up a delegate protocol with required and optional methods. If even 1 of those required methods really do not apply to how you are using their framework you will have to implement that method any and everywhere you want to be a delegate. The reason this is best case is because it is a framework and it likely won't require unnecessary methods. But if it did, you would be forced to create a wrapper around it. Forcing something to be wrapped does not promote code reuse. Now let's look a worse case example I'm sure we've all seen before.

Worst case reuse example

-(void)didCancel {
    // This won't happen since we don't have a cancel option in this context
}

Props to you if you've done this before (j/k) and even better if you've copied pasted it somewhere else too :). Don't feel bad if you have, I have too.

Ok so we can start to see the delegation pattern break down if we haven't made sure the required methods will really be required in every context. And when needing to reuse many times the complexity and coupling required is just too much. Let's say you have a Download Helper Class that will download a file to a path you specify. You could use the delegation pattern. The delegate tells the delegator (the downloader) to start a download and the delegator will call you back letting them know whether it succeeded or not. Now if you have separate methods for success and failure, you've really created a reuse nightmare. Now if you have 10 different places in the app where you are downloading content, they will have to go through all the boilerplate to set themseleves as a delegate. And not only that anything in local scope at the time of calling the download will have to be saved as member properties since the response will come in a separate method.

Better reuse alternative

Alright so here it is. The answer to all our problems: blocks (or Swift closures). It's a 3 step process: import the header, save a pointer to the delegator (if it does asyncronous work), and the call the method and handle the response in the completion block in one step!

[self.downloader downloadWithCompletion:^(BOOL didDownload) {
    if (didDownload) {
       // do something
    } else {
       // do something
    } 
}];

Look at that code above. Now this is what I call reusable code! Let's say worst case scenario you have a lot of logic you are doing in the completion block that will get copied in multiple places. If it isn't worth breaking out a wrapper class or giving more responsibility to the delegator then at least all of the code will be copied together without having to copy code from multiple places (member properties, delegate methods, code to set-up delegate). It would all be in one place.

Date: 2014-11-26T17:47+0000

Author: Korey Hinton

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0