Вот представьте что у нас есть фабрика. Но в отличии от фабрики из предыдущего поста, она умеет создавать только телефоны на базе андроида, и еще при этом различной конфигурации. То есть, есть один объект, но при этом его состояние может быть совершенно разным, а еще представьте если его очень трудно создавать, и во время создания этого объекта еще и создается миллион дочерних объектов. Именно в такие моменты, нам очень помогает такой паттерн как строитель.
Когда использовать:
- Создание сложного объекта.
- Процесс создания объекта тоже очень не тривиальный – к примеру получение данных из базы и манипуляция ими.
Сам паттерн состоит из двух компонент – 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"; }];
Немає коментарів:
Дописати коментар