Шаблоны программирования на Swift: Builder

builder_proSwift

Продолжаем изучать шаблоны программирования на Swift. И сегодня будем разбирать шаблон Builder.

Builder

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

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

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

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

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

//
// proSwift.ru
//

class AndriodPhone {
    var osVersion = ""
    var name = ""
    var cpuCodeName = ""
    var RAMsize = 0
    var osVersionCode = 0
    var launcher = ""
    
    
    func setOSVersion() {
        self.osVersion = ""
    }
    
    func setName() {
        self.name = ""
    }
    
    func setCPUCodeName() {
        self.cpuCodeName = ""
    }
    
    func setRAMsize() {
        self.RAMsize = 0
    }
    
    func setOSVersionCode() {
        self.osVersionCode = 0
    }
    
    func setLauncher() {
        self.osVersion = ""
    }
}

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

//
// proSwift.ru
//

class BPAndroidPhoneBuilder {

    let phone = AndriodPhone()
 
    
    func getPhone() -> AndriodPhone  {
        return self.phone
    }
    
}

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

//
// proSwift.ru
//

class LowPricePhoneBuilder: BPAndroidPhoneBuilder {
    func setOSVersion() {
        self.phone.osVersion = "Android 2.3"
    }
    
    func setName() {
        self.phone.name = "Ведрофон"
    }
    
    func setCPUCodeName() {
        self.phone.cpuCodeName = "Some shitty CPU"
    }
    
    func setRAMsize() {
        self.phone.RAMsize = 256
    }
    
    func setOSVersionCode() {
        self.phone.osVersionCode = 3
    }
    
    func setLauncher() {
        self.phone.launcher = "Hia Tsung"
    }
}

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

//
// proSwift.ru
//

class HighPricePhoneBuilder: BPAndroidPhoneBuilder {
    func setOSVersion() {
        self.phone.osVersion = "Android 5.0"
    }
    
    func setName() {
        self.phone.name = "Крутой лопатофон"
    }
    
    func setCPUCodeName() {
        self.phone.cpuCodeName = "Some shitty, but expensive CPU"
    }
    
    func setRAMsize() {
        self.phone.RAMsize = 2048
    }
    
    func setOSVersionCode() {
        self.phone.osVersionCode = 5
    }
    
    func setLauncher() {
        self.phone.launcher = "HTC Sence"
    }
}

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

//
// proSwift.ru
//

class FactorySalesMan {
    
    var builder = BPAndroidPhoneBuilder()
    
    func setBuilder(aBuilder: BPAndroidPhoneBuilder) {
        self.builder = aBuilder
    }
    
    func getPhone() -> AndriodPhone {
        return self.builder.getPhone()
    }
    

    
    func constuctPhone() {
        if let builder = builder as? LowPricePhoneBuilder {
            builder.setOSVersion()
            builder.setName()
            builder.setCPUCodeName()
            builder.setRAMsize()
            builder.setOSVersionCode()
            builder.setLauncher()
            
        } else if let builder = builder as? HighPricePhoneBuilder {
            builder.setOSVersion()
            builder.setName()
            builder.setCPUCodeName()
            builder.setRAMsize()
            builder.setOSVersionCode()
            builder.setLauncher()
        }
    }
}

Ну теперь давайте посмотрим что же у нас получилось:

//
// proSwift.ru
//

let cheapPhoneBuilder = LowPricePhoneBuilder()
let expensivePhoneBuilder = HighPricePhoneBuilder()

let director = FactorySalesMan()
director.setBuilder(cheapPhoneBuilder)
director.constuctPhone()
let phone = director.getPhone()
print("Phone name \(phone.name), phone cpu: \(phone.cpuCodeName)")

director.setBuilder(expensivePhoneBuilder)
director.constuctPhone()
let phone2 = director.getPhone()
print("Phone2 name \(phone2.name), phone cpu: \(phone2.cpuCodeName)")

И вывод в консоль:

Phone name Ведрофон, phone cpu: Some shitty CPU
Phone2 name Крутой лопатофон, phone cpu: Some shitty, but expensive CPU

Пример с GitHub

//
// proSwift.ru
//
// https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/creational/builder.swift

class DeathStarBuilder {

    var x: Double?
    var y: Double?
    var z: Double?

    typealias BuilderClosure = (DeathStarBuilder) -> ()

    init(buildClosure: BuilderClosure) {
        buildClosure(self)
    }
}

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

Сама Звезда Смерти инициализируется с помощью созданного нами строителя.

//
// proSwift.ru
//
// https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/creational/builder.swift

struct DeathStar : CustomStringConvertible {

    let x: Double
    let y: Double
    let z: Double

    init?(builder: DeathStarBuilder) {

        if let x = builder.x, y = builder.y, z = builder.z {
            self.x = x
            self.y = y
            self.z = z
        } else {
            return nil
        }
    }

    var description:String {
        return "Death Star at (x:\(x) y:\(y) z:\(z))"
    }
}

И теперь используем все что написали для создания объектов.

//
// proSwift.ru
//
// https://github.com/ochococo/Design-Patterns-In-Swift/blob/master/source/creational/builder.swift

let empire = DeathStarBuilder { builder in
    builder.x = 0.1
    builder.y = 0.2
    builder.z = 0.3
}

let deathStar = DeathStar(builder:empire)
print(deathStar?.description)  // Optional("Death Star at (x:0.1 y:0.2 z:0.3)")

Тут мы создаем строителя, путем передачи той самой функции, о которой говорилось выше. Эта функция присваивает значения координатам, но запросто могла бы вычислить сложные математические уравнения расчета движения космической станции в подпространстве.  А после получаем Звезду Смерти, используя строителя.

 

 

1 Comment on “Шаблоны программирования на Swift: Builder

  1. не очень удачный пример…
    в constuctPhone() идем switchCase анализ, нарушается OpenCl.P.

Добавить комментарий для Алексей Отменить ответ

Ваш адрес email не будет опубликован.

*

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.