Abstract Factory и Builder

Два порождающих паттерна для непростого создания: семейства связанных объектов (Abstract Factory) и пошаговая сборка (Builder).

Abstract Factory создаёт семейства связанных объектов, а Builder пошагово собирает один сложный объект, отделяя конструирование от представления.

Abstract Factory: согласованные семейства

Представьте UI-тему. Светлая и тёмная темы должны давать согласованный набор: тёмная кнопка с тёмным чекбоксом, а не вперемешку. Abstract Factory — это фабрика, которая отдаёт целое семейство объектов одного «стиля», гарантируя их совместимость.

from abc import ABC, abstractmethod


class Button(ABC):
    @abstractmethod
    def render(self): ...


class DarkButton(Button):
    def render(self): return "тёмная кнопка"


class LightButton(Button):
    def render(self): return "светлая кнопка"


class Theme(ABC):              # абстрактная фабрика
    @abstractmethod
    def button(self) -> Button: ...


class DarkTheme(Theme):
    def button(self): return DarkButton()


class LightTheme(Theme):
    def button(self): return LightButton()


def build_ui(theme: Theme):
    print(theme.button().render())


build_ui(DarkTheme())
build_ui(LightTheme())

Вывод:

тёмная кнопка
светлая кнопка

Каждая конкретная фабрика (DarkTheme, LightTheme) производит свой набор продуктов. Клиент выбирает фабрику один раз — и дальше получает гарантированно согласованные элементы. Реальные продукты обычно имеют несколько методов (кнопка, чекбокс, поле ввода).

Builder: пошаговая сборка

Когда у объекта много опциональных частей, конструктор с десятью аргументами становится нечитаемым. Builder задаёт сборку по шагам и в конце отдаёт готовый объект. Удобно делать методы «текучими» (возвращать self).

class Burger:
    def __init__(self):
        self.parts = []


class BurgerBuilder:
    def __init__(self):
        self._burger = Burger()

    def add(self, part):
        self._burger.parts.append(part)
        return self            # текучий интерфейс

    def build(self):
        return self._burger


burger = (BurgerBuilder()
          .add("булка")
          .add("котлета")
          .add("сыр")
          .build())

print(" + ".join(burger.parts))

Вывод:

булка + котлета + сыр

Чтение сборки идёт сверху вниз как рецепт. Builder особенно хорош, когда часть шагов опциональна или порядок важен (например, конфигурация HTTP-запроса, построение SQL).

Когда что выбрать

  • Abstract Factory — когда нужно создавать несколько видов объектов, которые должны быть согласованы между собой.
  • Builder — когда один объект сложный и собирается из многих опциональных частей.

Где встречается

Abstract Factory — кросс-платформенные UI-наборы, драйверы под разные СУБД. Builder — построители запросов (query builders), StringBuilder, конфигурации тестовых объектов, сборка сложных DTO.

Итог

  • Abstract Factory отдаёт согласованное семейство объектов.
  • Builder собирает один сложный объект пошагово, часто с текучим интерфейсом.
  • Оба отделяют клиента от деталей конструирования.
Проверьте себя
1. Чем Abstract Factory отличается от Factory Method?
AНичем
BAbstract Factory создаёт целое семейство согласованных объектов, а не один продукт
CAbstract Factory быстрее
DFactory Method работает только с одним языком
2. Когда Builder особенно уместен?
AКогда объект собирается из многих опциональных частей пошагово
BКогда нужен ровно один экземпляр
CКогда надо адаптировать чужой интерфейс
DКогда объект не имеет полей
3. Что обычно возвращают методы Builder для «текучего» интерфейса?
ANone
Bself (сам строитель)
Cстроку
Dготовый объект на каждом шаге
Поддержать проект