Приведи пример реализации паттерна Builder (Строитель) на PHP.

«Приведи пример реализации паттерна Builder (Строитель) на PHP.» — вопрос из категории Паттерны, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Builder отделяет конструирование сложного объекта от его представления, позволяя создавать разные представления объекта, используя один и тот же процесс построения.

Классическая реализация (из GoF):

// 1. Продукт, который мы строим
class Car
{
    private string $engine = '';
    private int $seats = 0;
    private bool $hasGPS = false;
    private bool $hasTripComputer = false;

    public function setEngine(string $engine): void { $this->engine = $engine; }
    public function setSeats(int $seats): void { $this->seats = $seats; }
    public function setGPS(bool $hasGPS): void { $this->hasGPS = $hasGPS; }
    public function setTripComputer(bool $hasTripComputer): void { $this->hasTripComputer = $hasTripComputer; }

    public function getDescription(): string
    {
        return sprintf(
            "Car with %s engine, %d seats%s%s.",
            $this->engine,
            $this->seats,
            $this->hasGPS ? ', GPS' : '',
            $this->hasTripComputer ? ', Trip Computer' : ''
        );
    }
}

// 2. Интерфейс Строителя
interface CarBuilder
{
    public function reset(): void;
    public function buildEngine(): void;
    public function buildSeats(): void;
    public function buildGPS(): void;
    public function buildTripComputer(): void;
    public function getResult(): Car;
}

// 3. Конкретный строитель для спортивного автомобиля
class SportsCarBuilder implements CarBuilder
{
    private Car $car;

    public function reset(): void { $this->car = new Car(); }
    public function buildEngine(): void { $this->car->setEngine('V8 Turbo'); }
    public function buildSeats(): void { $this->car->setSeats(2); }
    public function buildGPS(): void { $this->car->setGPS(true); } // В спортивной версии есть GPS
    public function buildTripComputer(): void { $this->car->setTripComputer(true); }
    public function getResult(): Car { return $this->car; }
}

// 4. Директор, который управляет процессом построения
class Director
{
    public function constructSportsCar(CarBuilder $builder): Car
    {
        $builder->reset();
        $builder->buildEngine();
        $builder->buildSeats();
        $builder->buildGPS();
        $builder->buildTripComputer();
        return $builder->getResult();
    }
}

// 5. Клиентский код
$builder = new SportsCarBuilder();
$director = new Director();

$car = $director->constructSportsCar($builder);
echo $car->getDescription(); // Car with V8 Turbo engine, 2 seats, GPS, Trip Computer.

Почему это полезно:

  • Изоляция сложной логики создания. Клиент (Director) не знает деталей сборки конкретной модели.
  • Гибкость. Можно легко добавить нового строителя для SUVBuilder с другим набором опций, используя тот же интерфейс.
  • Пошаговое конструирование. Методы build... можно вызывать в разном порядке или пропускать, если это позволяет строитель.