PHP Builder Design Pattern Порождающий паттерн

Паттерн Строитель в PHP

Короткий скринкаст: паттерн «Строитель» (Builder) — собираем неизменяемый объект по цепочке методов и не трогаем его конструктор.

Builder Pattern — открыть страницу урока

Описание

В этом скринкасте мы собираем пиццу без длинного конструктора. Класс Pizza неизменяем: все данные передаются в конструктор сразу. Класс PizzaBuilder накапливает параметры шагами (size(), addTopping()), каждый шаг возвращает $this для цепочки вызовов, а метод build() в конце создаёт готовый продукт.

UML-диаграмма

classDiagram class Pizza { -string size -array toppings +__construct(size: string, toppings: array) +describe() string } class PizzaBuilder { -string size = "medium" -array toppings = [] +size(size: string) PizzaBuilder +addTopping(topping: string) PizzaBuilder +build() Pizza } PizzaBuilder ..> Pizza : builds

Код

Pizza.php

class Pizza
{
    public function __construct(
        private readonly string $size,
        private readonly array $toppings
    ) {}

    public function describe(): string
    {
        return $this->size . ' pizza with ' . implode(', ', $this->toppings);
    }
}

PizzaBuilder.php

class PizzaBuilder
{
    private string $size = 'medium';
    private array $toppings = [];

    public function size(string $size): self
    {
        $this->size = $size;
        return $this;
    }

    public function addTopping(string $topping): self
    {
        $this->toppings[] = $topping;
        return $this;
    }

    public function build(): Pizza
    {
        return new Pizza($this->size, $this->toppings);
    }
}

demo.php

require_once 'Pizza.php';
require_once 'PizzaBuilder.php';

$pizza = (new PizzaBuilder())
    ->size('large')
    ->addTopping('pepperoni')
    ->addTopping('olives')
    ->build();

echo $pizza->describe();

Заметка: зачем Строитель

Длинные конструкторы с десятком параметров нечитаемы, а их перегрузки с «пропуском» аргументов называют телескопическими. Строитель решает обе проблемы: цепочка методов читается как предложение, а неизменяемый продукт получается в одном вызове build(). Плюс каждый шаг может проверить данные до того, как объект будет создан, — неудачный рецепт не даст родить «полуготовую» пиццу.

Пример результата

После php demo.php в терминале появится:

large pizza with pepperoni, olives

Преимущества

  • Позволяет собирать сложный объект пошагово и читаемо.
  • Отделяет конструирование от представления: один и тот же процесс даёт разные объекты.
  • Избавляет от «телескопических» конструкторов с десятком параметров.
  • Удобен для необязательных частей и значений по умолчанию.

Когда использовать

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

Использование в Laravel

В Laravel Строитель — это Illuminate\Database\Query\Builder и Illuminate\Database\Eloquent\Builder: методы where(), join() и orderBy() шаг за шагом собирают SQL-запрос, а выполняется он только при вызове get(). Тот же приём — в Illuminate\Bus\PendingBatch для настройки пачки задач и в Illuminate\Testing\TestResponse, где проверки выстраиваются цепочкой.