1.2. Builder

1.2.1. 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.

1.2.2. Examples

  • PHPUnit: Mock Builder

1.2.3. UML Diagram

Alt Builder UML Diagram

1.2.4. Code

You can also find this code on GitHub

Director.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder;
 6
 7use DesignPatterns\Creational\Builder\Parts\Vehicle;
 8
 9/**
10 * Director is part of the builder pattern. It knows the interface of the builder
11 * and builds a complex object with the help of the builder
12 *
13 * You can also inject many builders instead of one to build more complex objects
14 */
15class Director
16{
17    public function build(Builder $builder): Vehicle
18    {
19        $builder->createVehicle();
20        $builder->addDoors();
21        $builder->addEngine();
22        $builder->addWheel();
23
24        return $builder->getVehicle();
25    }
26}

Builder.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder;
 6
 7use DesignPatterns\Creational\Builder\Parts\Vehicle;
 8
 9interface Builder
10{
11    public function createVehicle(): void;
12
13    public function addWheel(): void;
14
15    public function addEngine(): void;
16
17    public function addDoors(): void;
18
19    public function getVehicle(): Vehicle;
20}

TruckBuilder.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder;
 6
 7use DesignPatterns\Creational\Builder\Parts\Door;
 8use DesignPatterns\Creational\Builder\Parts\Engine;
 9use DesignPatterns\Creational\Builder\Parts\Wheel;
10use DesignPatterns\Creational\Builder\Parts\Truck;
11use DesignPatterns\Creational\Builder\Parts\Vehicle;
12
13class TruckBuilder implements Builder
14{
15    private Truck $truck;
16
17    public function addDoors(): void
18    {
19        $this->truck->setPart('rightDoor', new Door());
20        $this->truck->setPart('leftDoor', new Door());
21    }
22
23    public function addEngine(): void
24    {
25        $this->truck->setPart('truckEngine', new Engine());
26    }
27
28    public function addWheel(): void
29    {
30        $this->truck->setPart('wheel1', new Wheel());
31        $this->truck->setPart('wheel2', new Wheel());
32        $this->truck->setPart('wheel3', new Wheel());
33        $this->truck->setPart('wheel4', new Wheel());
34        $this->truck->setPart('wheel5', new Wheel());
35        $this->truck->setPart('wheel6', new Wheel());
36    }
37
38    public function createVehicle(): void
39    {
40        $this->truck = new Truck();
41    }
42
43    public function getVehicle(): Vehicle
44    {
45        return $this->truck;
46    }
47}

CarBuilder.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder;
 6
 7use DesignPatterns\Creational\Builder\Parts\Door;
 8use DesignPatterns\Creational\Builder\Parts\Engine;
 9use DesignPatterns\Creational\Builder\Parts\Wheel;
10use DesignPatterns\Creational\Builder\Parts\Car;
11use DesignPatterns\Creational\Builder\Parts\Vehicle;
12
13class CarBuilder implements Builder
14{
15    private Car $car;
16
17    public function addDoors(): void
18    {
19        $this->car->setPart('rightDoor', new Door());
20        $this->car->setPart('leftDoor', new Door());
21        $this->car->setPart('trunkLid', new Door());
22    }
23
24    public function addEngine(): void
25    {
26        $this->car->setPart('engine', new Engine());
27    }
28
29    public function addWheel(): void
30    {
31        $this->car->setPart('wheelLF', new Wheel());
32        $this->car->setPart('wheelRF', new Wheel());
33        $this->car->setPart('wheelLR', new Wheel());
34        $this->car->setPart('wheelRR', new Wheel());
35    }
36
37    public function createVehicle(): void
38    {
39        $this->car = new Car();
40    }
41
42    public function getVehicle(): Vehicle
43    {
44        return $this->car;
45    }
46}

Parts/Vehicle.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder\Parts;
 6
 7abstract class Vehicle
 8{
 9    final public function setPart(string $key, object $value)
10    {
11    }
12}

Parts/Truck.php

1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Creational\Builder\Parts;
6
7class Truck extends Vehicle
8{
9}

Parts/Car.php

1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Creational\Builder\Parts;
6
7class Car extends Vehicle
8{
9}

Parts/Engine.php

1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Creational\Builder\Parts;
6
7class Engine
8{
9}

Parts/Wheel.php

1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Creational\Builder\Parts;
6
7class Wheel
8{
9}

Parts/Door.php

1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Creational\Builder\Parts;
6
7class Door
8{
9}

1.2.5. Test

Tests/DirectorTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Creational\Builder\Tests;
 6
 7use DesignPatterns\Creational\Builder\Parts\Car;
 8use DesignPatterns\Creational\Builder\Parts\Truck;
 9use DesignPatterns\Creational\Builder\TruckBuilder;
10use DesignPatterns\Creational\Builder\CarBuilder;
11use DesignPatterns\Creational\Builder\Director;
12use PHPUnit\Framework\TestCase;
13
14class DirectorTest extends TestCase
15{
16    public function testCanBuildTruck()
17    {
18        $truckBuilder = new TruckBuilder();
19        $newVehicle = (new Director())->build($truckBuilder);
20
21        $this->assertInstanceOf(Truck::class, $newVehicle);
22    }
23
24    public function testCanBuildCar()
25    {
26        $carBuilder = new CarBuilder();
27        $newVehicle = (new Director())->build($carBuilder);
28
29        $this->assertInstanceOf(Car::class, $newVehicle);
30    }
31}