вівторок, 20 червня 2017 р.

Abstract Factory

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

Итак, что же делает абстрактная фабрика: Абстрактная фабрик дает простой интерфейс для создания объектов которые принадлежат к тому или иному сеймейству объектов.

Отличия от фабричного метода:

  1. Фабричный метод порождает объекты одного и того же типа, фабрика же может создавать независимые объекты.
  2. Чтобы добавить новый тип объекта – надо поменять интерфейс фабрики, в фабричном методе же легко просто поменять внутренности метода, который ответственный за порождение объектов.

Давайте представим ситуацию: у нас есть две фабрики по производству iPhone и iPad. Одна оригинальная, компании Apple, другая – хижина дядюшки Хуа. И вот, мы хотим производить эти товары: если в страны 3-го мира – то товар от дядюшки, в другие страны – товар любезно предоставлен компанией Apple.

Итак, пускай у нас есть фабрика, которая умеет производить и айпады и айфоны:

@class ASGenericIPhone, ASGenericIPad;

@interface ASIPhoneFactory : NSObject

- (ASGenericIPhone *)iPhone;
- (ASGenericIPad *)iPad;

@end

Естественно, нам необходимо реализовать продукты которые фабрика будет производить:

@interface ASGenericIPad : NSObject

@property (nonatomic, copy) NSString *osName;
@property (nonatomic, copy) NSString *productName;
@property (nonatomic, strong) NSNumber *screenSize;

@end
@interface ASGenericIPhone : NSObject

@property (nonatomic, copy) NSString *osName;
@property (nonatomic, copy) NSString *productName;

@end

Но, продукты немного отличаются.

Пускай у нас есть два типа продуктов, оригинальные Apple и продукты которые произведены трутолюбивым дядюшкой Хуа:

@interface ASAppleIPad : ASGenericIPad

@end
@implementation ASAppleIPad

- (instancetype)init {
    self = [super init];
    
    if (self) {
        self.productName = @"iPad";
        self.osName = @"iOS";
        self.screenSize = @9.7;
    }
    
    return self;
}

@end
@interface ASAppleIPhone : ASGenericIPhone

@end
@implementation ASAppleIPhone

- (instancetype)init {
    self = [super init];
    
    if (self) {
        self.productName = @"iPhone";
        self.osName = @"iOS";
    }
    
    return self;
}

@end

Дядюшкофоны:

@interface ASChinaPad : ASGenericIPad

@end
@implementation ASChinaPad

- (instancetype)init {
    self = [super init];
    
    if (self) {
        self.productName = @"Buan Que Ipado Killa";
        self.osName = @"Windows CE";
        self.screenSize = @12.5;
    }
    
    return self;
}

@end
@interface ASChinaPhone : ASGenericIPhone

@end
@implementation ASChinaPhone

- (instancetype)init {
    self = [super init];
    
    if (self) {
        self.productName = @"Chi Huan Hua Phone";
        self.osName = @"Android";
    }
    
    return self;
}

@end

Разные телефоны, конечно же, производятся на различных фабриках, потому мы просто обязанны их создать! Приблизительно так должны выглядеть фабрика Apple:

@interface ASAppleFactory : ASIPhoneFactory

#import "ASAppleIPad.h"
#import "ASAppleIPhone.h"

@implementation ASAppleFactory

- (ASGenericIPad *)iPad {
    return [ASAppleIPad new];
}

- (ASGenericIPhone *)iPhone {
    return [ASAppleIPhone new];
}

Конечно же у нашего китайского дядюшки тоже есть своя фабрика:

@interface ASChinaFactory : ASIPhoneFactory

@end
#import "ASChinaPad.h"
#import "ASChinaPhone.h"

@implementation ASChinaFactory

- (ASGenericIPad *)iPad {
    return [ASChinaPad new];
}

- (ASGenericIPhone *)iPhone {
    return [ASChinaPhone new];
}

@end

Как видим, фабрики одинаковые, а вот девайсы у них получются разные wink

Вот собственно и все, мы приготовили все что надо для доменострации! Теперь, давайте напишем небольшой метод который будет возвращать нам фабрику которую мы хотим (кстати, тут фабричный метод таки будет):

- (ASIPhoneFactory *)factory {    
    return self.isThirdWorld ? [ASChinaFactory new] : [ASAppleFactory new];
}

Теперь, давайте как создадим несколько телефонов:

    self.isThirdWorld = YES;
    ASIPhoneFactory *factory = self.factory;
    ASGenericIPad *ipad = factory.iPad;
    ASGenericIPhone *iphone = factory.iPhone;
    NSLog(@"iPad named = %@, osname = %@, screensize = %.1f",
          ipad.productName, ipad.osName, ipad.screenSize.doubleValue);
    NSLog(@"iPhone named = %@, osname = %@", iphone.productName, iphone.osName);

Лог будет выглядить следующим образом:

2017-06-20 21:47:34.670 PatternsObjC[1210:44956] iPad named = Buan Que Ipado Killa, osname = Windows CE, screensize = 12.5
2017-06-20 21:47:34.671 PatternsObjC[1210:44956] iPhone named = Chi Huan Hua Phone, osname = Android

Теперь, просто поменяв значение проперти isThirdWorld на NO, и лог будет совсем другой:

2017-06-20 21:44:55.887 PatternsObjC[1167:38662] iPad named = iPad, osname = iOS, screensize = 9.7
2017-06-20 21:44:55.888 PatternsObjC[1167:38662] iPhone named = iPhone, osname = iOS

Пример Abstract Factory в Foundation, iOS SDK - классы NSNumber, NSData, NSArray, NSDictionary, NSString. Рассмотрим на примере NSNumber, эта фабрика возвращает такие классы - NSCFNumber, NSCFBoolean и др.

Немає коментарів:

Дописати коментар

HyperComments for Blogger

comments powered by HyperComments