Статическая фабрика на Objective-C

Паттерн проектирования Фабрика предназначается для создания объектов.

Паттерн Фабрика используется когда нужно:
  • создавать множество объектов разделяющих между собой один интерфейс;
  • создавать комплексные объекты;
  • скрыть конкретные классы и реализации от клиентского кода;
  • предоставить единообразный способ создания одного или группы объектов.  
Пример:

ConsoleOutput это интерфейс для logging-а текста в консоли.

//
// ConsoleOutput.h // #import <Foundation/Foundation.h> @protocol ConsoleOutput <NSObject> - (void)outputText:(NSString *)aContent; @end  
PrintLog это имплементация интерфейса ConsoleOutput, использующая функцию fprintf.

//
//  PrintLog.h
//

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

@interface PrintLog : NSObject<ConsoleOutput>

@end


//
//  PrintLog.m
//

#import "PrintLog.h"

@implementation PrintLog

- (void)outputText:(NSString *)aContent;
{
    fprintf(stdout, "%s\n", [aContent UTF8String]);
}

@end
 
LogOutput это другая имплементация интерфейса ConsoleOutput, использующая функцию NSLog.

//
//  LogOutput.h
//

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

@interface LogOutput : NSObject<ConsoleOutput>

@end


//
//  LogOutput.m
//

#import "LogOutput.h"

@implementation LogOutput

- (void)outputText:(NSString *)aContent;
{
    NSLog(@"%@", aContent);
}

@end

Теперь нам нужна Фабрика для того чтобы клиент мог выбрать имплементацию без знания типов возвращаемых объектов. Клиента не волнует, что он получит при том условии, что ему известен интерфейс. Клиент работает с интерфейсом, а не с имплементацией.

Одна из ключевых принципов объектно-ориентированного проектирования для повторного использования: программируйте в соответствии с интерфейсом, а не с реализацией. Не объявляйте переменные как экземпляры конкретных классов. Вместо этого придерживайтесь интерфейса, определенного абстрактным классом.

Также Фабрика помогает сократить количество импортируемых заголовочных файлов.

//
//  OutputFactory.h
//

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

typedef NS_ENUM(NSInteger, OutputType)
{
    OutputTypeTimestamp,
    OutputTypeNoTimestamp
};

@interface OutputFactory : NSObject

+ (id<ConsoleOutput>)create:(OutputType)aType;

@end


//
//  OutputFactory.m
//

#import "OutputFactory.h"
#import "LogOutput.h"
#import "PrintLog.h"

@implementation OutputFactory

+ (id<ConsoleOutput>)create:(OutputType)aType
{
    switch (aType)
    {
        case OutputTypeTimestamp:
            return [[LogOutput alloc] init];
        case OutputTypeNoTimestamp:
            return  [[PrintLog alloc] init];
    }
}

@end

В main.m нам нужно импортировать только один файл OutputFactory.h. Также мы получили единообразный подход к созданию объектов. 

//
//  main.m
//

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

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

    @autoreleasepool {
        
        id theWithTimestamp = [OutputFactory create:OutputTypeTimestamp];
        [theWithTimestamp outputText:@"Output without a time stamp"];
        
        id theNoTimestamp = [OutputFactory create:OutputTypeNoTimestamp];
        [theNoTimestamp outputText:@"Without a time stamp"];
        
    }
    return 0;
}

Вывод:
2014-12-18 18:01:58.040 MyExample[2803:303] Output without a time stamp
Without a time stamp
Program ended with exit code: 0

Паттерн Фабрика очень подходит для использования в различных API. Потому что пользователям API не интересно внутреннее устройство вашего API, они просто хотят получить определенный тип функциональности и Фабрика предоставляет его.

--