Re: Why use protocols?
Re: Why use protocols?
- Subject: Re: Why use protocols?
- From: Ondra Cada <email@hidden>
- Date: Wed, 18 May 2005 20:03:05 +0200
Matt,
On 18.5.2005, at 19:07, Matt Neuburg wrote:
Here's an example. I've subclassed NSTableColumn in order to override
dataCellForRow:. But I don't think it's my NSTableColumn subclass's
job to
contain the implementation; it's my table data source's job. So all my
NSTableColumn subclass does is turn to the data source to get the
answer:
@implementation MyTableColumn
- (id)dataCellForRow:(int)row {
id ourDataSource = [[self tableView] dataSource];
return [ourDataSource dataCellForRow: row sender: self];
}
@end
Now, how do we know that ourDataSource (which, it is important to
stress,
could be any class whatever) implements dataCellForRow:sender:? And
how does
the compiler know? A protocol solves both problems at once.
It does, but (in the Cocoa worlds) not quite the right way. It is
better to use informal protocols here, and, where appropriate, check
whether it responds to a given selector.
It prevents the
programmer from forgetting to implement dataCellForRow:sender: in
the data
source class (it would be a disaster if the programmer forgot to do
this);
It does not: only instead of forgeting to implement the method he
forgets to add the protocol to the class declaration :)
and it shuts the compiler up by satisfying it that ourDataSource
does in
fact implement it.
@protocol MyTableColumnDataSource
- (id) dataCellForRow: (int) row sender: (id) self;
@end
@implementation MyTableColumn
- (id)dataCellForRow:(int)row {
id <MyTableColumnDataSource> ourDataSource = [[self tableView]
dataSource];
This would issue a warning anyway, unless you have overridden
tableView to return id<MyTableColumnDataSource>. And, what's worse,
it would not help a bit if the programmer forgot (to use the protocol
and implement the method).
If you truly wanted to use formal protocols (which is not the best
idea), you should check here and cast:
...
id ds=[[self tableView] dataSource];
if (![ds conformsToProtocol:@protocol(MyTableColumnDataSource)])
@throw @"Oops, not MyTableColumnDataSource"; //Q&D
id<MyTableColumnDataSource> myDs=(id<MyTableColumnDataSource>)ds;
...
return [ourDataSource dataCellForRow: row sender: self];
}
@end
*Much* better solution would be something like this:
@interface NSObject (MyTableColumnDataSource)
-dataCellForRow:(int)row sender:sender; // it's not best practice to
name an argument 'self'
@end
@implementation MyTableColumn
-dataCellForRow:(int)row {
id ds=[[self tableView] dataSource];
if ([ds respondsToSelector:@selector(dataCellForRow:sender:)])
return [ds dataCellForRow:row sender:self];
return [super dataCellForRow:row];
}
...
Note that not only this is more flexible (by automatically falling
back to the standard NSTableColumn behaviour if the data source does
not support the extension), but also it allows the programmer to use
any existing data source: this might not be possible with a formal
protocol conformance, if the programmer has no access to the
controller sources:
#import "TheCurrentTableControllerWhoseSourcesIDontHave.h"
@implementation TheCurrentTableControllerWhoseSourcesIDontHave
(MyTableColumnDataSource)
-dataCellForRow:(int)row sender:sender {
...
}
@end
This concept works excellently with informal protocols, but would not
be (reasonably) possible with formal ones, since you can't (in an
easy and clean way) add a protocol to a class which you are not
compiling.
---
Ondra Čada
OCSoftware: email@hidden http://www.ocs.cz
private email@hidden http://www.ocs.cz/oc
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden