My previous post describes a controller for separating an
application's model from Cappuccino's CPOutlineView
. But
there's actually one place where the model and view kind of meet up:
- (id)outlineView:(CPOutlineView)outlineView objectValueForTableColumn:(CPTableColumn)tableColumn byItem:(id)item
{
return (nil == item) ? @"" : [item objectValueForOutlineColumn: tableColumn];
}
I decided to have the controller give a model class access to a column, which is part of the view. Why?
This example has four kinds of columns and three kinds of model classes. Each column wants to display each kind of model element differently. One obvious solution is to use a conditional expression:
if the column is a ... and the model element is a ... then ...
But even simple object-oriented languages like Objective-J have easy mechanisms and patterns for reducing the need to test objects for their class or whether they implement some specific message. Going way back in Smalltalk lore, there's Double Dispatch.
The goal is to reveal to a column the presentation object to use in
each of its rows. To do so, the column
view, CPTableColumn
, is subclassed for each kind of
column in the example's outline:
SDNavigationColumn
SDTopicColumn
SDNotesColumn
SDDateColumn
Each of these subclasses implements the following methods:
objectValueForTopLevel:
objectValueForPriority:
objectValueForStuff:
Each of the model classes
implements objectValueForOutlineColumn:
by sending one of
the above messages to the given column. For
example, SDStuff
is implemented as follows:
- (id)objectValueForOutlineColumn:(id)anOutlineColumn
{
return [anOutlineColumn objectValueForStuff: self];
}
This dispatches right back ("double dispatch") to have the column respond with the awareness that it is on a row for detailed stuff. Each column will be interested in different aspects of the stuff. The navigation column is only interested in hierarchy and not details. Here is its implementation, which just returns the empty string to indicate its disinterest:
- (id)objectValueForStuff:(SDStuff)aStuff
{
return "";
}
On the other hand the date column is interested in a presentation of the given date:
- (id)objectValueForStuff:(SDStuff)aStuff
{
return [[[aStuff date] description] substringToIndex: 10];
}
Although this kind of brings the model and the view together, the names could be refactored because there is nothing really "column-specific" about this mini-protocol. Also Objective-J has "categories" for grouping methods. The intent of this protocol could be explained by placing the methods in a "presenting" category.
There are certainly more elaborate mechanisms available for keeping views and models separated, yet in-sync. Double Dispatch is just a simple one for reducing the need to test for classes and messages before sending them.
No comments:
Post a Comment