PHP 生成器模式

目的

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

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

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

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

 例子

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

 UML 图

Alt Builder UML Diagram

 代码

Director.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

/**
 * Director is part of the builder pattern. It knows the interface of the builder
 * and builds a complex object with the help of the builder
 *
 * You can also inject many builders instead of one to build more complex objects
 */
class Director{
    public function build(Builder $builder): Vehicle{
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

Builder.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

interface Builder{
    public function createVehicle();
    public function addWheel();
    public function addEngine();
    public function addDoors();
    public function getVehicle(): Vehicle;
}

TruckBuilder.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Wheel;
use DesignPatterns\Creational\Builder\Parts\Truck;
use DesignPatterns\Creational\Builder\Parts\Vehicle;

class TruckBuilder implements Builder{
    private Truck $truck;
    public function addDoors(){
        $this->truck->setPart('rightDoor', new Door());
        $this->truck->setPart('leftDoor', new Door());
    }
    public function addEngine(){
        $this->truck->setPart('truckEngine', new Engine());
    }
    public function addWheel(){
        $this->truck->setPart('wheel1', new Wheel());
        $this->truck->setPart('wheel2', new Wheel());
        $this->truck->setPart('wheel3', new Wheel());
        $this->truck->setPart('wheel4', new Wheel());
        $this->truck->setPart('wheel5', new Wheel());
        $this->truck->setPart('wheel6', new Wheel());
    }
    public function createVehicle(){
        $this->truck = new Truck();
    }
    public function getVehicle(): Vehicle{
        return $this->truck;
    }
}

CarBuilder.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Wheel;
use DesignPatterns\Creational\Builder\Parts\Car;
use DesignPatterns\Creational\Builder\Parts\Vehicle;

class CarBuilder implements Builder{
    private Car $car;

    public function addDoors(){
        $this->car->setPart('rightDoor', new Door());
        $this->car->setPart('leftDoor', new Door());
        $this->car->setPart('trunkLid', new Door());
    }

    public function addEngine(){
        $this->car->setPart('engine', new Engine());
    }

    public function addWheel(){
        $this->car->setPart('wheelLF', new Wheel());
        $this->car->setPart('wheelRF', new Wheel());
        $this->car->setPart('wheelLR', new Wheel());
        $this->car->setPart('wheelRR', new Wheel());
    }

    public function createVehicle(){
        $this->car = new Car();
    }

    public function getVehicle(): Vehicle{
        return $this->car;
    }
}

Parts/Vehicle.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

abstract class Vehicle{
    public function setPart(string $key, object $value){}
}

Parts/Truck.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

class Truck extends Vehicle{}

Parts/Car.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

class Car extends Vehicle{}

Parts/Engine.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

class Engine{}

Parts/Wheel.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

class Wheel{}

Parts/Door.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Parts;

class Door{}

 测试

Tests/DirectorTest.php

<?php
declare(strict_types=1);

namespace DesignPatterns\Creational\Builder\Tests;

use DesignPatterns\Creational\Builder\Parts\Car;
use DesignPatterns\Creational\Builder\Parts\Truck;
use DesignPatterns\Creational\Builder\TruckBuilder;
use DesignPatterns\Creational\Builder\CarBuilder;
use DesignPatterns\Creational\Builder\Director;
use PHPUnit\Framework\TestCase;

class DirectorTest extends TestCase{
    public function testCanBuildTruck(){
        $truckBuilder = new TruckBuilder();
        $newVehicle = (new Director())->build($truckBuilder);

        $this->assertInstanceOf(Truck::class, $newVehicle);
    }

    public function testCanBuildCar(){
        $carBuilder = new CarBuilder();
        $newVehicle = (new Director())->build($carBuilder);

        $this->assertInstanceOf(Car::class, $newVehicle);
    }
}