В чем разница между паттернами Builder и Factory?

Ответ

Оба паттерна относятся к порождающим, но решают разные задачи создания объектов.

Factory (Фабрика): Сосредоточена на инкапсуляции логики выбора конкретного класса для создания. Ее цель — скрыть детали инстанцирования от клиента.

  • Акцент: «Что» создать.
  • Процесс: Создание объекта обычно происходит в один шаг.
  • Используется, когда есть общий интерфейс или базовый класс и несколько конкретных реализаций, выбор между которыми зависит от условий.
// Простая фабрика
protocol Vehicle {
    func drive()
}

class Car: Vehicle { /* ... */ }
class Truck: Vehicle { /* ... */ }

enum VehicleFactory {
    static func makeVehicle(for load: Int) -> Vehicle {
        // Логика выбора конкретного класса
        return load > 1000 ? Truck() : Car()
    }
}
// Клиентский код
let vehicle = VehicleFactory.makeVehicle(for: 500)
vehicle.drive() // Не знает, создалась Car или Truck

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

  • Акцент: «Как» создать, с какими параметрами.
  • Процесс: Пошаговое конфигурирование, часто с использованием fluent-интерфейса (цепочка вызовов), финализируемое методом build().
  • Используется, когда объект имеет много опциональных или взаимозависимых полей, и его создание в одном конструкторе было бы громоздким (телескопический конструктор).
// Builder для сложного объекта
class Pizza {
    let size: Size
    let cheese: Bool
    let pepperoni: Bool
    // ... много других свойств
    // Инициализатор может быть private
    init(builder: PizzaBuilder) { /* ... */ }
}

class PizzaBuilder {
    private var size: Size = .medium
    private var cheese = true
    private var pepperoni = false

    func setSize(_ size: Size) -> PizzaBuilder {
        self.size = size
        return self // Fluent interface
    }
    func addPepperoni() -> PizzaBuilder {
        self.pepperoni = true
        return self
    }
    func removeCheese() -> PizzaBuilder {
        self.cheese = false
        return self
    }

    func build() -> Pizza {
        // Здесь может быть валидация параметров
        return Pizza(builder: self)
    }
}
// Клиентский код
let pizza = PizzaBuilder()
    .setSize(.large)
    .addPepperoni()
    .removeCheese()
    .build() // Объект создается здесь

Итог: Используйте Factory, когда нужно делегировать выбор класса. Используйте Builder, когда нужно гибко и наглядно конфигурировать сложный объект.

Ответ 18+ 🔞

Давай разжую, как есть, без соплей. Оба паттерна — про создание объектов, но мозги они ебут по-разному, вот в чём соль.

Фабрика (Factory) — это как умный автомат с выбором. Её задача — спрятать от тебя всю хуйню с логикой «а какой именно класс сейчас нужно создать». Ты ей говоришь: «Хочу транспорт!», а она уже сама решает, дать тебе легковушку или грузовик, в зависимости от тоннажа. Акцент на «ЧТО» создать. Всё происходит в один присест.

// Та самая умная выборка
protocol Vehicle { func drive() }
class Car: Vehicle {}
class Truck: Vehicle {}

enum VehicleFactory {
    static func makeVehicle(for load: Int) -> Vehicle {
        // Вот она, логика, спрятанная в жопе фабрики
        return load > 1000 ? Truck() : Car()
    }
}
// Ты, как клиент, нихуя не паришься
let vehicle = VehicleFactory.makeVehicle(for: 500)
vehicle.drive() // Поехал, а на чём — похуй, это фабрикина головная боль

Строитель (Builder) — это уже другая, блядь, опера. Тут объект не «выбирается», а «собирается по кирпичикам». Представь, что ты заказываешь пиццу: размер, сыр, колбаса, грибы, ананасы, мать их... И если всё это запихнуть в один конструктор, получится пиздец на 15 параметров, половина из которых опциональны. Вот строитель как раз позволяет настраивать всё пошагово, красиво, с цепочкой вызовов. Акцент на «КАК» создать, с какими опциями.

// Строитель для этой вашей многокомпонентной хуйни
class Pizza {
    let size: Size
    let cheese: Bool
    let pepperoni: Bool
    // ... и ещё овердохуища свойств
    init(builder: PizzaBuilder) { /* ... */ } // Часто приватный, чтоб не лезли
}

class PizzaBuilder {
    private var size: Size = .medium
    private var cheese = true
    private var pepperoni = false

    func setSize(_ size: Size) -> PizzaBuilder {
        self.size = size
        return self // Это и есть тот самый fluent-интерфейс, цепочка
    }
    func addPepperoni() -> PizzaBuilder {
        self.pepperoni = true
        return self
    }
    func removeCheese() -> PizzaBuilder {
        self.cheese = false
        return self
    }

    func build() -> Pizza {
        // Тут, блядь, может быть валидация — всё ли намешали правильно
        return Pizza(builder: self) // Финальный аккорд, объект рождается тут
    }
}
// А вот как этим пользоваться, красота же!
let pizza = PizzaBuilder()
    .setSize(.large)
    .addPepperoni()
    .removeCheese() // Вот этот чувак сыр не любит
    .build() // И только здесь пицца материализуется

Короче, резюме, чтобы не ебать мозг:

  • Тыкаешь в Фабрику, когда тебе нужно, чтобы кто-то умный за тебя выбрал конкретный класс из кучи вариантов. «Сделай мне транспорт» — и поехал.
  • Хватаешься за Строитель, когда объект — сложная хуйня с кучей настроек, и ты хочешь собирать его поэтапно, как конструктор. «Дайте пиццу, но без сыра, зато с перцем и оливками» — вот это его стезя.

Всё. Выбор за тобой, просто не путай, а то получится ебушки-воробушки.