1.2. 生成器模式

1.2.1. 目的

生成器模式(Builder,或称建造者模式)是一个接口,用于构建复杂对象的各个部分。

在某些情况下,如果生成器对其构建的内容有很好的了解,那么这个接口可以是一个抽象类,并会有一个默认方法(也称适配器)。

如果对象具有复杂的继承结构,那么按照的正常逻辑,生成器也应该有一个复杂的继承结构。

注意:生成器通常有都有一个完善的接口,例如 PHPUnit 的模拟生成器(Mock Builder)。

1.2.2. 例子

  • PHPUnit: 模拟生成器(Mock Builder)

1.2.3. UML 图

Alt Builder UML Diagram

1.2.4. 代码

你可以在 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. 测试

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}