Patterns in Objective-C: Strategy Pattern using Forwarding

When working with the Observer Pattern and the Singleton Pattern it is easy to see how they relate to the Java or C++ versions of the same concept. Everything basically plugs into the same places you would expect, coming from another Object Oriented language. The implementation isn’t all that different.

We are going to see something a little more interesting when working with the Strategy Pattern in Objective-C. The Strategy Pattern is one of the most basic patterns and is even covered in the introduction to Head First Design Patterns. While the concept is identical in Objective-C as it is in Java and C++, the implementation is much closer to languages with first-class functions such as Python.

To do this, we will take advantage of one of the more powerful features of Objective-C: Forwarding.

The basic concept behind the strategy pattern is that you have a Context and a Strategy. The Context contains the Strategy and delegates its own behavior to it.

Using an example adapted out of the Head First book, you might have a Bird object. Different birds have different flying behaviors, so you have a FlyingBehavior strategy. The Bird then has a fly method that calls the fly method on FlyingBehavior. Thus the Bird‘s flying behavior is encapsulated by an implementation of the FlyingBehavior protocol.

In Objective-C we can take it one step further, and automatically forward messages to the desired method (this also comes in handy for the Proxy pattern, see NSProxy).

To implement this forwarding system, it requires three things:

  1. A member variable where the strategy is stored.
  2. Overloading forwardInvocation:
  3. Overloading methodSignatureForSelector:

For example:

Header File

1
2
3
4
5
6
7
8
9
#import <Cocoa/Cocoa.h>
 
@interface Context : NSObject {
	id strategy;
}
 
- (id)initWithStrategy:(id)_strategy;
 
@end

Class File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#import "Context.h"
 
@implementation Context
 
- (id)initWithStrategy:(id)_strategy {
	if( self = [super init] ) {
		//The strategy can be of any class.
		strategy = _strategy;
	}
 
	return self;
}
 
- (void)forwardInvocation:(NSInvocation *)invocation {
	SEL selector = [invocation selector];
 
	if( [strategy respondsToSelector:selector] ) {
		[invocation invokeWithTarget:strategy];
	} else {
		[self doesNotRecognizeSelector:selector];
	}
}
 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
	if( [[self class] instancesRespondToSelector:selector] ) {
		//Get the instance method signature from the Context's class.
		return [[self class] instanceMethodSignatureForSelector:selector];
	} else if( [strategy respondsToSelector:selector] ) {
		//Get the instance method signature from the Strategy.
		return [strategy methodSignatureForSelector:selector];
	}
	//If neither the strategy nor the context respond to the selector,
	//then we return nil.
	return nil;
}
 
@end

It is possible to discriminate between multiple strategies using the forwardInvocation: call, but this represents the simplest version.

The “non-forwarding” version of the Strategy Pattern is implemented exactly how it would be in C++.

This entry was posted in Uncategorized. Bookmark the permalink.

Error: The ad management script is not properly configured for this user

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>