Что это
Паттерн Строитель собирает сложный объект шаг за шагом. Вместо одного конструктора с длинным непонятным списком аргументов строитель предлагает небольшие именованные шаги — и завершающий build(), который возвращает готовый объект.
В PHP шаги возвращают $this, поэтому вызовы складываются в цепочку, читающуюся как понятная фраза.
Пример: сборка пиццы
Сравните (new PizzaBuilder("large"))->cheese()->topping("mushrooms") с конструктором вида new Pizza("large", true, [...]) — строитель прямо говорит, что означает каждое значение, а необязательные части просто пропускаются.
Шаги, складывающиеся в цепочку
Каждый шаг имеет имя и необязателен: маленькая пицца пропускает их все. Никакой мешанины аргументов true, null, false — а добавки выводятся обычным списком через запятую и как none, когда их нет.
Вывод: PHP-скрипт
<?php
class Pizza {
public function __construct(
public string $size,
public bool $cheese,
public array $toppings,
) {}
}
class PizzaBuilder {
private bool $cheese = false;
private array $toppings = [];
public function __construct(private string $size) {}
public function cheese(): self {
$this->cheese = true;
return $this;
}
public function topping(string $t): self {
$this->toppings[] = $t;
return $this;
}
public function build(): Pizza {
return new Pizza($this->size, $this->cheese, $this->toppings);
}
}
function describe(Pizza $p): void {
$toppings = $p->toppings === [] ? "none" : implode(", ", $p->toppings);
printf("%s pizza, cheese: %s, toppings: %s\n",
$p->size, var_export($p->cheese, true), $toppings);
}
$big = (new PizzaBuilder("large"))
->cheese()
->topping("mushrooms")
->topping("olives")
->build();
$small = (new PizzaBuilder("small"))->build();
describe($big);
describe($small);
Когда его применять
Применяйте, когда у объекта много необязательных частей или когда построение состоит из настоящих шагов, которым стоит дать имена. Запросы, SQL-выборки и конфигурации — классическая территория строителя.
Обойдитесь без него для простых объектов — именованные аргументы PHP, new Pizza(size: "large"), и так делают обычный конструктор хорошо читаемым в простых случаях.
Плюсы и минусы
Плюсы: читаемое построение; необязательные части без гигантских списков аргументов; объект можно проверить один раз — в build().
Минусы: ещё один класс, который надо написать и поддерживать; излишество там, где обычный конструктор и так читается хорошо.