Builder Pattern

Purpose

Builder is an interface that build parts of a complex object.

Sometimes, if the builder has a better knowledge of what it builds, this interface could be an abstract class with default methods (aka adapter).

If you have a complex inheritance tree for objects, it is logical to have a complex inheritance tree for builders too.

Note: Builders have often a fluent interface, see the mock builder of PHPUnit for example.

Examples
  • PHPUnit: Mock Builder
UML


Code

Builder


namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

interface Builder
{
    public function createVehicle();
    public function addWheel();
    public function addEngine();
    public function addDoors();
    public function getVehicle(): Vehicle;
}

CarBuilder


namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Wheel;
use DesignPatterns\Creational\Builder\Parts\Car;
use DesignPatterns\Creational\Builder\Parts\Vehicle;

class CarBuilder implements Builder
{
    private Car $car;

    public function addDoors()
    {
        $this->car->setPart('rightDoor', new Door());
        $this->car->setPart('leftDoor', new Door());
        $this->car->setPart('trunkLid', new Door());
    }

    public function addEngine()
    {
        $this->car->setPart('engine', new Engine());
    }

    public function addWheel()
    {
        $this->car->setPart('wheelLF', new Wheel());
        $this->car->setPart('wheelRF', new Wheel());
        $this->car->setPart('wheelLR', new Wheel());
        $this->car->setPart('wheelRR', new Wheel());
    }

    public function createVehicle()
    {
        $this->car = new Car();
    }

    public function getVehicle(): Vehicle
    {
        return $this->car;
    }
}

TruckBuilder


namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Wheel;
use DesignPatterns\Creational\Builder\Parts\Truck;
use DesignPatterns\Creational\Builder\Parts\Vehicle;

class TruckBuilder implements Builder
{
    private Truck $truck;

    public function addDoors()
    {
        $this->truck->setPart('rightDoor', new Door());
        $this->truck->setPart('leftDoor', new Door());
    }

    public function addEngine()
    {
        $this->truck->setPart('truckEngine', new Engine());
    }

    public function addWheel()
    {
        $this->truck->setPart('wheel1', new Wheel());
        $this->truck->setPart('wheel2', new Wheel());
        $this->truck->setPart('wheel3', new Wheel());
        $this->truck->setPart('wheel4', new Wheel());
        $this->truck->setPart('wheel5', new Wheel());
        $this->truck->setPart('wheel6', new Wheel());
    }

    public function createVehicle()
    {
        $this->truck = new Truck();
    }

    public function getVehicle(): Vehicle
    {
        return $this->truck;
    }
}

Director


namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

/**
* Director is part of the builder pattern. It knows the interface of the builder
* and builds a complex object with the help of the builder
* 
* You can also inject many builders instead of one to build more complex objects
*/
class Director
{
    public function build(Builder $builder): Vehicle
    {
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

Vehicle


namespace DesignPatterns\Creational\Builder\Parts;

abstract class Vehicle
{
    final public function setPart(string $key, object $value)
    {
    }
}

Car


namespace DesignPatterns\Creational\Builder\Parts;

class Car extends Vehicle
{
}

Truck


namespace DesignPatterns\Creational\Builder\Parts;

class Truck extends Vehicle
{
}

Parts - Door


namespace DesignPatterns\Creational\Builder\Parts;

class Door
{
}

Parts - Engine


namespace DesignPatterns\Creational\Builder\Parts;

class Engine
{
}

Parts - Wheel


namespace DesignPatterns\Creational\Builder\Parts;

class Wheel
{
}

Tests


public function testCanBuildTruck()
{
    $truckBuilder = new TruckBuilder();
    $newVehicle = (new Director())->build($truckBuilder);

    $this->assertInstanceOf(Truck::class, $newVehicle);
}

public function testCanBuildCar()
{
    $carBuilder = new CarBuilder();
    $newVehicle = (new Director())->build($carBuilder);

    $this->assertInstanceOf(Car::class, $newVehicle);
}

Ready to bring your vision to life?

We believe in excellence, empathy, integrity, and transparency throughout the process. Our goal is to build fast, responsive websites that not only perform but also reflect your values and vision.