Skip to content

iOS Design Patterns, Part 2 – Categories

July 5, 2012

This second installment of the series will focus on useful patterns for configuring lightweight components together.

Extend standard classes with categories to provide commonly used implementations

A great example I’ve found in API client components is the need for a hashed representation of a string value. In order to prevent code duplication and provide a common facility for that function, there are several effective mechanisms. One might create a utility class or add a class method to a common shared class in their code base. Another more effective technique is to use a category to inject the desired behavior.

Not recommended:

+ (NSString*)hashedValueForString:(NSString*)aString;

Suggested alternative:

@interface NSString (Hash)
-(NSString*)hashedValue;
@end

@implementation NSString (Hash)
- (NSString*)hashedValue
{
  return [SHA1 hexdigest:self]; // or some other hash generating library
}
@end

The category makes this a powerful pattern. Categories must be included explicitly in a class in order to make use of their interface methods. This means we can call hashedValue on any NSString object, as long as we include the category header. Without the category structure, we would be forced to extend NSString and replace all relevant references to our subclass instead. That often means a lot of refactoring, during which references can be easily overlooked. This eliminates the need for a tight coupling between these commonly used objects.

Observant readers will have noticed by now that we are using the “not recommended” pattern in our “suggested alternative” category implementation. The point of this technique is to reduce complexity at the interface level. The class method approach is a perfectly suitable mechanism for computing the value. We’re simply extending one useful functional programming pattern by wrapping it in another one.

Define shared behavior on multiple types with a shared category

Another common pattern in mobile data processing is processing sets and arrays of heterogenous object types. In other words, arrays containing strings, booleans, numbers, and binary objects. It’s impossible for all these types to inherit from a common class. It’s also unrealistic to extend every type and refactor all the references in your code to use the subclasses instead. Using categories, we can inject methods with a common signature into each of the various types we’re working with. Then, our processing code can iterate through the collection, calling a common method on each element.

Example:

@interface NSString (WeightFunction)
-(float)weight;
@end
@interface NSNumber (WeightFunction)
-(float)weight;
@end
@interface NSArray (WeightFunction)
-(float)weight;
@end

// inside your visitor code
#import "NSString+WeightFunction.h"
#import "NSNumber+WeightFunction.h"
#import "NSArray+WeightFunction.h"

for (id obj in mixedCollection) {
  float tWeight = [obj weight];
  // do something with tWeight
}

Include context reference for additional configurability

As an extension to the previous section, sometimes it’s useful to convey some context through the interface layer. That is easily achieved using an argument in the category method.

Example:

@interface NSString (WeightFunction)
-(float)weightInArray:(NSArray*)aArray;
@end
@interface NSNumber (WeightFunction)
-(float)weightInArray:(NSArray*)aArray;
@end
@interface NSArray (WeightFunction)
-(float)weightInArray:(NSArray*)aArray;
@end

// inside your visitor code
#import "NSString+WeightFunction.h"
#import "NSNumber+WeightFunction.h"
#import "NSArray+WeightFunction.h"

for (id obj in mixedCollection) {
  float tWeight = [obj weightInArray:mixedCollection];
  // do something with tWeight
}
About these ads
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 123 other followers

%d bloggers like this: