1.2. Constructor

1.2.1. Propósito

El constructor es una interfaz que construye parte de un objeto complejo.

A veces, si el constructor conoce bien lo que está construyendo, esta interfaz podría ser una clase abstracta con métodos por defecto (también conocido como Adaptador)

Si tienes una herencia compleja de tu árbol de objetos, es lógico tener también una herencia compleja en el árbol de constructores.

Nota: Los constructores suelen tener una interfaz fluida, fíjate en el mock builder de PHPUnit por ejemplo.

1.2.2. Ejemplos

  • PHPUnit: Mock Builder

1.2.3. Diagrama UML

Alt Builder UML Diagram

1.2.4. Código

Puedes encontrar el código en 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}