Что это такое
Паттерн Строитель (Builder) создаёт сложный объект шаг за шагом. Вместо одного конструктора с длинным и запутанным списком аргументов строитель предлагает небольшие именованные шаги — и завершающий build(), который возвращает готовый объект.
Каждый шаг возвращает самого строителя, поэтому вызовы складываются в цепочку, которая читается как предложение.
Пример: собираем пиццу
Сравните PizzaBuilder("large").cheese().topping("mushrooms") с вызовом конструктора вида Pizza("large", True, ["mushrooms"]) — строитель говорит, что означает каждое значение, а необязательные части просто пропускаются.
Шаги, которые складываются в цепочку
Каждый шаг именован и необязателен: маленькая пицца (small) пропускает их все. Никакой каши из аргументов True, None, False.
Вывод: полная программа на Python
from dataclasses import dataclass, field
@dataclass
class Pizza:
size: str
cheese: bool = False
toppings: list[str] = field(default_factory=list)
class PizzaBuilder:
def __init__(self, size: str):
self._pizza = Pizza(size)
def cheese(self) -> "PizzaBuilder":
self._pizza.cheese = True
return self
def topping(self, t: str) -> "PizzaBuilder":
self._pizza.toppings.append(t)
return self
def build(self) -> Pizza:
return self._pizza
big = (
PizzaBuilder("large")
.cheese()
.topping("mushrooms")
.topping("olives")
.build()
)
small = PizzaBuilder("small").build()
print(f"Пицца: {big.size}, сыр: {big.cheese}, начинки: {big.toppings}")
print(f"Пицца: {small.size}, сыр: {small.cheese}, начинки: {small.toppings}")
Когда его использовать
Используйте его, когда у объекта много необязательных частей или когда создание состоит из настоящих шагов, заслуживающих имён. Запросы, выборки из базы данных и конфигурации — классическая область применения строителя.
Не используйте его для простых объектов — вызов с именованными аргументами, Pizza(size="large"), уже сам по себе хороший маленький строитель для простых случаев.
Плюсы и минусы
Плюсы: понятное создание объекта; необязательные части без огромных списков аргументов; объект можно проверить один раз, в build().
Минусы: ещё один класс, который нужно написать и поддерживать; излишество там, где именованные аргументы и так читаются хорошо.