On specializing the protocol of a delegate in the subclass of an abstract class

Jeff Lemarche, one-half of the dynamic duo behind the excellent Beginning iPhone Development book and also part-time Twitter prankster has an interesting post up on Differences in Delegation between Cocoa and Cocoa Touch (or, more specifically, a review of the merits of using formal vs. informal protocols for delegates).

Today, I ran across a use case where, unless I'm missing something, using an informal protocol is the only viable solution. Take this scenario:

You have an abstract base class (e.g., AbstractDataOperation) which has several concreate subclasses (e.g., ConcreteDataOperationA and ConcreteDataOperationB). AbstractDataOperation defines the delegate member and has one call that it makes on it (e.g., dataOperationDidFail:). The concrete subclasses has several other calls that they make on the delegate above and beyond the one call in the method on the abstract class (e.g., concreateDataOperationADidSucceed, concreteDataOperationBDidSucceed).

The easy way out, of course, is simply to define delegate as type id. This, however, won't give you any assistance during develoment and won't warn you about possible problems in your code.

Instead, you can define a formal protocol (e.g., DataOperationProtocol) that the delegate can conform to in the abstract class (e.g., id <DataOperationProtocol> delegate). This works well until you get to implementing the concrete subclasses. Whereas the delegates of the concrete subclasses must conform to DataOperationProtocol, more than likely you will also want them to conform to a specialized DataOperationProtocol like ConcreteDataOperationProtocolA <DataOperationProtocol>. AFAICS, since you cannot redefine fields in subclasses, there's no way to handle this use case using a formal protocol.

The alternative? Do it the old-fashioned Cocoa way and declare your delegates using informal protocols (categories on NSObject). You can then import the subset of the protocol you need in your abstract class, create different informal protocols for your specialized concrete classes, and combine the informal protocols (thereby in a way "extending" the informal protocol) by importing the general delegate classes (informal protocols) from the specialized ones. e.g.,

#import <Foundation/Foundation.h>

#import "DataOperationDelegate.h"

@interface NSObject(ConcreteDataOperationDelegateA)

// "Inherits" from DataOperationDelegate:
// -(void)dataOperationDidFail:(NSError *)error from DataOperationDelegate informal protocol/category.

// "Extends" it with:
-(void)concreateDataOperationADidSucceed;

@end

Thoughts?

Comments