1.2. Builder

1.2.1. Zweck

Builder ist ein Interface, das Teile eines komplexen Objekts aufbaut.

Manchmal, wenn der Builder ein gutes Bild davon hat, was er bauen solle, dann kann dieses Interface aus auch einer abstrakten Klasse mit Standardmethoden bestehen (siehe Adapter).

Wenn du einen komplexen Vererbungsbaum für ein Objekt hast, ist es nur logisch, dass auch der Builder über eine komplexe Vererbungshierarchie verfügt.

Hinweis: Builder haben oft auch ein Fluent Interface, siehe z.B. der Mockbuilder in PHPUnit.

1.2.2. Beispiele

  • PHPUnit: Mock Builder

1.2.3. UML Diagramm

Alt Builder UML Diagram

1.2.4. Code

Du findest den Code auch auf 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. Теst

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}