Back in September, I wrote about the strategy design pattern and suggested that it could be useful in iPhone and iPad programming to provide alternate implementations of a piece of functionality. When thinking about strategy, think “plugin”. Strategy implies a relationship between classes where a class has a strategy object to do specific work. In object-oriented terminology, this is aggregation, where an object “has-a” reference to a worker.
There is another style of objects working together called composition. In this case, an object “is-a” derivative of another. A classic implementation of composition is the template method design pattern and it probably won’t surprise you to learn that there are applications of this in iPhone and iPad programming too.
NSFetchedResultsController
When working with Core Data on iOS, there is a very useful class called NSFetchedResultsController
that can be used to monitor changes to a Core Data data model and update the user-interface, in particular a table-view, automatically.
I won’t go into the details, because that’s not the point of the post, but if you want to know more, fetched results controllers are a new topic in the new revision of Learning Tree’s Building iPhone® and iPad® Applications: Extended Features course. (Work on which has been distracting me from blog posts over the past few weeks but is now in the final stages of testing and should be available to run from the end of November).
One of the advantages of the fetched results controller is that it standardizes the table view data source methods. For example:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[self.fetchedResultsController sections] count]; }
Nothing specific in there. No references to the type of object the table view is displaying and so on. So the temptation when creating another table view controller is to reach for copy and paste. The alarm bells start ringing — there must be a better way …
… and there is. You can make a superclass and push these methods up into it. Xcode even has refactoring tools to help you.
Template Method
It all goes swimmingly well until you get to the tableView:cellForRowAtIndexPath method. This is where the specifics of your table view controller, i.e. the specific data it displays, start to bite. You could simply choose not to push that method up into the superclass.
MyTableViewController.m
- (UITableViewCell *)tableView:(UITabelView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath ]; // Configure the cell SomeData *someData = [self.fetchedResultsController objectAtIndexPath:indexPath ]; cell.textLabel.text = someData.someProperty; cell.detailTextLabel.text = someData.someOtherProperty; return cell; }
But here’s where the template method comes in. The idea of the template method pattern is to take a standard algorithm and provide a customization option through subclassing. The standard algorithm in this case is:
If we put that algorithm into the superclass we created, we can create a template method that we can override in the subclass and it will be called in the correct place:
BaseTableViewController.m
- (UITableViewCell *)tableView:(UITabelView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath ]; // The template method [self configureCell:cell]; return cell; } - (void)configureCell:(UITableViewCell *)cell { // Do nothing, this method is overridden by the subclass. We need it here // or this class won't compile. In a language like Java, we would // make this an abstract method. In C++ terms, pure-virtual. abort(); // Fail if this method is not subclassed }
So in our table view controller that is a subclass of the BaseTableViewController
:
MyTableViewController.n
- (void)configureCell:(UITableViewCell *)cell { SomeData *someData = [self.fetchedResultsController objectAtIndexPath:indexPath ]; cell.textLabel.text = someData.someProperty; cell.detailTextLabel.text = someData.someOtherProperty; }
The MyTableViewController
class inherits the standard methods that we pushed up but provides a specific implementation of the configureCell:
method
Now you might be thinking that’s an algorithm that is too simple to get wrong and probably doesn’t warrant the use of a template method. You may be right but it’s the principle of the template method that I’m trying to illustrate: an algorithm defined in a base class where the customization point in a subclass is achieved by providing an implementation of the template method.
Once you understand the pattern, hopefully you will recognize when to apply it in more complex algorithms where one step needs to be customized but the rest of the algorithm is constant.