пʼятницю, 23 червня 2017 р.

Builder

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

Когда использовать:

  1. Создание сложного объекта.
  2. Процесс создания объекта тоже очень не тривиальный – к примеру получение данных из базы и манипуляция ими.

Сам паттерн состоит из двух компонент – Bulilder и Director. Builder занимается именно построением объекта, а Director знает какой Builder использовать чтобы выдать необходимый продукт. Приступим!

Пускай у нас есть телефон, который обладает следующими свойствами:

@interface ASAndroidPhone : NSObject

@property (nonatomic, copy) NSString *osVersion;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *cpuCodeName;
@property (nonatomic, strong) NSNumber *RAMsize;
@property (nonatomic, strong) NSNumber *osVersionCode;
@property (nonatomic, copy) NSString *launcher;

@end

Давайте создадим дженерик строителя от которого будут наследоваться конкретные строители:

@class ASAndroidPhone;

@interface ASAndroidPhoneBuilder : NSObject

@property (nonatomic, strong, readonly) ASAndroidPhone *phone;

- (void)setOSVersion;
- (void)setName;
- (void)setCPUCodeName;
- (void)setRAMSize;
- (void)setOSVersionCode;
- (void)setLauncher;

@end
#import "ASAndroidPhone.h"

@interface ASAndroidPhoneBuilder ()

@property (nonatomic, strong) ASAndroidPhone *phone;

@end

@implementation ASAndroidPhoneBuilder

- (instancetype)init {
    self = [super init];
    
    if (self) {
        _phone = [ASAndroidPhone new];
    }
    
    return self;
}

- (void)setOSVersion {
    
}

- (void)setName {
    
}

- (void)setCPUCodeName {
    
}

- (void)setRAMSize {
    
}

- (void)setOSVersionCode {
    
}

- (void)setLauncher {
    
}

@end

А теперь напишем код для конкретных строителий. К примеру, так бы выглядел строитель для дешевого телефона:

@interface ASLowPricePhoneBuilder : ASAndroidPhoneBuilder

#import "ASAndroidPhone.h"

@implementation ASLowPricePhoneBuilder

- (void)setOSVersion {
    self.phone.osVersion = @"Android 2.3";
}

- (void)setName {
    self.phone.name = @"Low price phone!";
}

- (void)setCPUCodeName {
    self.phone.cpuCodeName = @"Some shitty CPU";
}

- (void)setRAMSize {
    self.phone.RAMsize = @256;
}

- (void)setOSVersionCode {
    self.phone.osVersionCode = @3.0;
}

- (void)setLauncher {
    self.phone.launcher = @"Hia Tsung!";
}

@end

И конечно же строительство дорогого телефона:

@interface ASHighPricePhoneBuilder : ASAndroidPhoneBuilder

@end
#import "ASAndroidPhone.h"

@implementation ASHighPricePhoneBuilder

- (void)setOSVersion {
    self.phone.osVersion = @"Android 7.1";
}

- (void)setName {
    self.phone.name = @"High price phone!";
}

- (void)setCPUCodeName {
    self.phone.cpuCodeName = @"Some shitty but expensive CPU";
}

- (void)setRAMSize {
    self.phone.RAMsize = @6144;
}

- (void)setOSVersionCode {
    self.phone.osVersionCode = @7.1;
}

- (void)setLauncher {
    self.phone.launcher = @"Samsung Launcher!";
}

@end

Кто-то же должен использовать строителей, потому давайте создадим объект который будет с помощью строителей создавать дешевые или дорогие телефоны:

@class ASAndroidPhone, ASAndroidPhoneBuilder;

@interface ASFactorySalesMan : NSObject

- (void)setBuilder:(ASAndroidPhoneBuilder *)builder;

- (ASAndroidPhone *)phone;
- (void)constructPhone;

@end
#import "ASAndroidPhone.h"
#import "ASAndroidPhoneBuilder.h"

@interface ASFactorySalesMan ()

@property (nonatomic, strong) ASAndroidPhoneBuilder *builder;

@end

@implementation ASFactorySalesMan

- (ASAndroidPhone *)phone {
    return self.builder.phone;
}

- (void)constructPhone {
    [self.builder setOSVersion];
    [self.builder setName];
    [self.builder setCPUCodeName];
    [self.builder setRAMSize];
    [self.builder setOSVersionCode];
    [self.builder setLauncher];
}

Ну и конечно же куда мы без теста и кода:

    ASLowPricePhoneBuilder *cheapPhoneBuilder = [ASLowPricePhoneBuilder new];
    ASHighPricePhoneBuilder *expensivePhoneBuilder = [ASHighPricePhoneBuilder new];
    
    ASFactorySalesMan *salesMan = [ASFactorySalesMan new];
    [salesMan setBuilder:cheapPhoneBuilder];
    [salesMan constructPhone];
    
    ASAndroidPhone *phone = [salesMan phone];
    
    NSLog(@"Phone Name = %@, osVersion = %@, cpu code name = %@, ram size = %@, os version code = %@, launcher = %@", phone.name, phone.osVersion, phone.cpuCodeName, phone.RAMsize, phone.osVersionCode, phone.launcher);
    
    [salesMan setBuilder:expensivePhoneBuilder];
    [salesMan constructPhone];
    
    phone = [salesMan phone];
    
    NSLog(@"Phone Name = %@, osVersion = %@, cpu code name = %@, ram size = %@, os version code = %@, launcher = %@", phone.name, phone.osVersion, phone.cpuCodeName, phone.RAMsize, phone.osVersionCode, phone.launcher);

Как видим, мы создали различных строителей, и сказав директору (ASFactorySalesMan) какой строитель мы хотим использовать, мы получаем тот девайс который нам необходим.

Традиционный лог:

2017-06-22 23:17:16.889 PatternsObjC[1207:41570] Phone Name = Low price phone!, osVersion = Android 2.3, cpu code name = Some shitty CPU, ram size = 256, os version code = 3, launcher = Hia Tsung!
2017-06-22 23:17:16.889 PatternsObjC[1207:41570] Phone Name = High price phone!, osVersion = Android 7.1, cpu code name = Some shitty but expensive CPU, ram size = 6144, os version code = 7.1, launcher = Samsung Launcher!

Пример Builder в Foundation, iOS SDK - классы NSURL, NSDate. Рассмотрим на примере NSURL.

NSURL *url = [NSURL URLWithBuilderBlock:^(NSURLComponents *builder) {
  builder.scheme = @"http";
  builder.host = @"programming086.blogspot.com";
  builder.path = @"/2017/06/builder.html";
}];

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

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

HyperComments for Blogger

comments powered by HyperComments