Что такое паттерн проектирования Builder (Строитель)?

Ответ

Builder (Строитель) — это порождающий паттерн, который позволяет создавать сложные объекты пошагово. Он отделяет конструирование объекта от его представления, так что один и тот же процесс построения может создавать разные представления.

Проблема: Конструктор объекта с множеством параметров (особенно опциональных) становится громоздким и нечитаемым ("телескопический конструктор").

Решение: Вынести процесс конструирования в отдельный объект-строитель.

Классическая реализация на Swift:

// Продукт, который мы строим
class Pizza {
    let size: Int
    let cheese: Bool
    let pepperoni: Bool
    let mushrooms: Bool

    init(builder: PizzaBuilder) {
        self.size = builder.size
        self.cheese = builder.cheese
        self.pepperoni = builder.pepperoni
        self.mushrooms = builder.mushrooms
    }
}

// Строитель
class PizzaBuilder {
    var size: Int = 12
    var cheese: Bool = false
    var pepperoni: Bool = false
    var mushrooms: Bool = false

    func setSize(_ size: Int) -> PizzaBuilder {
        self.size = size
        return self
    }

    func addCheese() -> PizzaBuilder {
        self.cheese = true
        return self
    }

    func addPepperoni() -> PizzaBuilder {
        self.pepperoni = true
        return self
    }

    func addMushrooms() -> PizzaBuilder {
        self.mushrooms = true
        return self
    }

    func build() -> Pizza {
        return Pizza(builder: self)
    }
}

// Использование (Fluent Interface)
let myPizza = PizzaBuilder()
    .setSize(16)
    .addCheese()
    .addPepperoni()
    .build()

Director (Распорядитель): Часто используется для определения типовых последовательностей шагов построения.

class PizzaDirector {
    func createMargherita(builder: PizzaBuilder) -> Pizza {
        return builder
            .setSize(12)
            .addCheese()
            .build()
    }

    func createPepperoniFeast(builder: PizzaBuilder) -> Pizza {
        return builder
            .setSize(16)
            .addCheese()
            .addPepperoni()
            .build()
    }
}

Преимущества:

  • Пошаговое создание сложных объектов.
  • Изоляция сложного кода сборки.
  • Повторное использование процесса конструирования.
  • Улучшение читаемости кода создания объекта.

Ответ 18+ 🔞

Да ты посмотри, что эти умники придумали! Builder, блядь! Ну реально, смотри.

Вот представь: тебе надо собрать пиццу. А пицца — это ж не просто хлеб с кетчупом, это, блядь, целая архитектура! Размер, сыр, пепперони, грибы... И если ты всё это в один конструктор сунешь, получится пиздец, а не код. Телескопический конструктор, ёпта! Сто параметров, половина из них опциональные, и ты каждый раз гадаешь, что там передаёшь на пятой позиции — грибы или оливки? Волнение ебать, терпения ноль ебать!

А они взяли и вынесли всю эту хуйню в отдельного чувака — Строителя. Гениально же!

class Pizza {
    let size: Int
    let cheese: Bool
    let pepperoni: Bool
    let mushrooms: Bool

    init(builder: PizzaBuilder) {
        self.size = builder.size
        self.cheese = builder.cheese
        self.pepperoni = builder.pepperoni
        self.mushrooms = builder.mushrooms
    }
}

Видишь? Сам объект Pizza стал простой, как валенок. Он тупо берет готовые свойства у строителя и говорит: «О, спасибо, братан, я готов». А всю грязную работу по настройке делает отдельный класс.

И вот он, главный герой — PizzaBuilder:

class PizzaBuilder {
    var size: Int = 12
    var cheese: Bool = false
    var pepperoni: Bool = false
    var mushrooms: Bool = false

    func setSize(_ size: Int) -> PizzaBuilder {
        self.size = size
        return self
    }

    func addCheese() -> PizzaBuilder {
        self.cheese = true
        return self
    }

    func addPepperoni() -> PizzaBuilder {
        self.pepperoni = true
        return self
    }

    func addMushrooms() -> PizzaBuilder {
        self.mushrooms = true
        return self
    }

    func build() -> Pizza {
        return Pizza(builder: self)
    }
}

Каждый метод настраивает один параметр и возвращает самого себя! Это называется Fluent Interface, а по-нашему — цепочка вызовов. Красота, блядь!

Собираешь пиццу, как в конструкторе:

let myPizza = PizzaBuilder()
    .setSize(16)
    .addCheese()
    .addPepperoni()
    .build()

Читается, как стихи: «Возьми строитель, поставь размер 16, добавь сыра, добавь пепперони, собери». Никакой магии, всё прозрачно. Удивление пиздец, как же просто!

А ещё есть, блядь, Директор! Это такой надсмотрщик, который знает типовые рецепты. Чтобы каждый раз не объяснять, как собрать «Маргариту».

class PizzaDirector {
    func createMargherita(builder: PizzaBuilder) -> Pizza {
        return builder
            .setSize(12)
            .addCheese()
            .build()
    }

    func createPepperoniFeast(builder: PizzaBuilder) -> Pizza {
        return builder
            .setSize(16)
            .addCheese()
            .addPepperoni()
            .build()
    }
}

Пришел, выбрал рецепт, получил пиццу. Не хухры-мухры!

Итоги, блядь, почему это охуенно:

  1. Собираешь сложные объекты по шагам, не грузя мозг.
  2. Код сборки изолирован — если что-то сломается, ищешь в одном месте.
  3. Один и тот же процесс можешь переиспользовать для разных результатов.
  4. Читаемость зашкаливает! Сразу видно, что и как собирается.

Вот так вот, блядь. Кажется, мелочь, а без нее потом в своем же коде разбираться — чих-пых тебя в сраку.