Michael Crosby


Objective C Design Patterns - Factory

Design patterns can help you solve complex coding problems with proven solutions. This will be a mini series about different design pattern implementations in Objective - C. I will be using a simple console app and updating the code on github with each new pattern.

Factory Pattern

This pattern provides a consistent way for object creation. A factory provides one point of creation for one or a group of objects. It also helps to not code to a concrete class.

When to use it? Many objects need created that share an interface Creation of complex objects Hide concrete classes and implementation from client code Provide one point of entry for the creation of a class or group of classes

Static Factory

Most of the time I find myself using a static factory instead of an abstract factory. A static factory is what we are going to focus on now. In the console app I have an interface for logging text to the console. There are two implementations for this interface. One uses NSLog and the other uses fprintf().

#import <Foundation/Foundation.h>

@protocol CMConsoleOutput <NSObject>

- (void) outputText: (NSString *) content;

@end


//Class one
#import "CMPrintLog.h"

@implementation CMPrintLog

- (void) outputText:(NSString *)content {

    fprintf(stdout, "%s\n", [content UTF8String]);
}

@end


//Class two
#import "CMLogOutput.h"

@implementation CMLogOutput

- (void) outputText:(NSString *)content {
    NSLog(content);
}

@end

Now we need a factory to choose what implementation the client should use without knowing the exact types of objects that are returned. The client does not care what they get as long as it implements the interface. It also helps to cut down the number of imports you have in your header files. You no longer have to import 5 different class headers; you just have to import one factory header. I went ahead an created the factory class file, typedef for identifying what type of output object the client wants, and a static method for creating/returning the object.

typedef enum ouputTypes {
    Timestamp,
    NoTimestamp
} OutputTypes;


@interface CMOutputFactory : NSObject

+ (id<CMConsoleOutput>) create: (OutputTypes) type;

@end

Now the only thing that we need to remember is to autorelease the objects that we create if you are not using ARC.

@implementation CMOutputFactory

+(id<CMConsoleOutput>) create:(OutputTypes)type {
    switch (type) {
        case Timestamp:
            return [[CMLogOutput alloc] init];
        case NoTimestamp:
            return  [[CMPrintLog alloc] init];
    }
}

@end

Finally we just need to use our new factory.

#import <Foundation/Foundation.h>
#import "CMOutputFactory.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        id withTimestamp = [CMOutputFactory create:Timestamp];
        id noTimestamp = [CMOutputFactory create:NoTimestamp];

        [withTimestamp outputText:@"Output without a time stamp"];

        [noTimestamp outputText:@"Without a time stamp"];
    }
    return 0;
}

Very easy, we only have one import and a consistent way to create objects. It is very straight forward for people consuming an API's classes when you provide a factory for object creation. They do not need to know the inner workings of your API, they just want a certain type of functionality and your factory provides that. If you have any questions just ask.

comments powered by Disqus