Builder Pattern
Purpose
The purpose of the design pattern
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
Examples of how the design pattern can be used
- PHPUnit: Mock Builder
UML
UML design pattern diagram

Code
Code snippets
Builder
Builder Interface which concrete builder classes implement.
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
CarBuilder is a concrete subclass implementing the Builder Interface. A new Instance of Car is created when the createVehicle method is called, the CarBuilder ‘add’ methods attach objects to the Car class.
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
TruckBuilder is a concrete subclass implementing the Builder Interface. A new Instance of Truck is created when the createVehicle method is called, the TruckBuilder ‘add’ methods attach objects to the Truck class.
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
The Director is part of the builder pattern. The build method accepts a class instance of Builder (so in this example either TruckBuilder or CarBuilder) then builds and returns the vehicle.
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
Handles the boilerplate code across the Vehicle concrete subclasses (Car and Truck) which extend this abstract class.
namespace DesignPatterns\Creational\Builder\Parts;
abstract class Vehicle
{
final public function setPart(string $key, object $value)
{
}
}
Car
The Car Class which extends Vehicle. The CarBuilder createVehicle returns this object, then the builders ‘add’ methods attach parts to it.
namespace DesignPatterns\Creational\Builder\Parts;
class Car extends Vehicle
{
}
Truck
The Truck Class which extends Vehicle. The TruckBuilder createVehicle returns this object, then the builders ‘add’ methods attach parts to it.
namespace DesignPatterns\Creational\Builder\Parts;
class Truck extends Vehicle
{
}
Parts - Door
Door is a class which gets attached via the builder to Vehicle subclass.
namespace DesignPatterns\Creational\Builder\Parts;
class Door
{
}
Parts - Engine
Engine is a class which gets attached via the builder to Vehicle subclass.
namespace DesignPatterns\Creational\Builder\Parts;
class Engine
{
}
Parts - Wheel
Wheel is a class which gets attached via the builder to Vehicle subclass.
namespace DesignPatterns\Creational\Builder\Parts;
class Wheel
{
}
Tests
Examples of how you’d build a Car and a Truck.
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.