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

«В чем разница между паттернами Builder и Factory?» — вопрос из категории Паттерны, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

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, когда нужно гибко и наглядно конфигурировать сложный объект.