Абстрактная фабрика

Паттерны проектирования / Порождающие паттерны
image

//abstract product A
@protocol IProductA <NSObject>
- (void)testA;
@end

//abstract product B
@protocol IProductB <NSObject>
- (void)testB;
@end

//abstract factory
@protocol IFactory <NSObject>
- (id<IProductA>)createA;
- (id<IProductB>)createB;
@end

//concrete product A1
@interface ProductA1 : NSObject <IProductA>
@end

@implementation ProductA1
- (void)testA {
    NSLog(@"test A1");
}
@end

//concrete product A2
@interface ProductA2 : NSObject <IProductA>
@end

@implementation ProductA2
- (void)testA {
    NSLog(@"test A2");
}
@end

//concrete product B1
@interface ProductB1 : NSObject <IProductB>
@end

@implementation ProductB1
- (void)testB {
    NSLog(@"test B1");
}
@end

//concrete product B2
@interface ProductB2 : NSObject <IProductB>
@end

@implementation ProductB2
- (void)testB {
    NSLog(@"test B2");
}
@end

//concrete factory 1
@interface Factory1 : NSObject <IFactory>
@end

@implementation Factory1

- (id<IProductA>)createA {
    return [[ProductA1 alloc] init];
}

- (id<IProductB>)createB {
    return [[ProductB1 alloc] init];
}
@end

//concrete factory 2
@interface Factory2 : NSObject <IFactory>
@end

@implementation Factory2

- (id<IProductA>)createA {
    return [[ProductA2 alloc] init];
}

- (id<IProductB>)createB {
    return [[ProductB2 alloc] init];
}
@end

//client code
void testFactory(id<IFactory> factory) {
    id<IProductA> productA = [factory createA];
    id<IProductB> productB = [factory createB];
    [productA testA];
    [productB testB];
}

testFactory([[Factory1 alloc] init]);
//printed: test A1
//         test B1
testFactory([[Factory2 alloc] init]);
//printed: test A2
//         test B2


Предоставляет интерфейс для создания семейств объектов, интерфейсы которых известны, а конкретные классы нет.
Read more

Задача. Программа рисует окна в двух стилях, светлом и тёмном. Каждому стилю нужны своя кнопка и своё меню. Если код создаёт их напрямую, он может по ошибке соединить светлую кнопку с тёмным меню. А третий стиль заставит искать каждое место, где создаётся кнопка или меню.

Идея. Абстрактная фабрика - это интерфейс, в котором на каждый вид продукта есть свой метод создания. Каждая конкретная фабрика реализует его для одного семейства. Одна фабрика создаёт светлые кнопку и меню, другая - тёмные. Клиент получает фабрику и создаёт через неё все продукты. Конкретный продукт он никогда не называет, поэтому всё, что он получает, принадлежит одному семейству. Чтобы сменить семейство целиком, клиенту дают другую фабрику.

Как читать пример. Продукты бывают двух видов, A и B. Конкретная фабрика выпускает одно семейство: по одному продукту каждого вида, и у обоих один и тот же номер. Клиент получает фабрику, просит у неё по продукту каждого вида и пользуется обоими. Какое семейство ему досталось, клиент не знает, но вывод это показывает. В некоторых языках интерфейсов не объявляют вовсе, но роли остаются теми же.

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

Частая ошибка. Взять этот паттерн, пока виды продуктов ещё часто меняются. Каждый новый вид добавляет метод и в абстрактную фабрику, и в каждую конкретную.