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
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}