建造者模式(Builder)

目的

建造者是创建一个复杂对象的一部分接口。

有时候,如果建造者对他所创建的东西拥有较好的知识储备,这个接口就可能成为一个有默认方法的抽象类(又称为适配器)。

如果对象有复杂的继承树,那么对于建造者来说,有一个复杂继承树也是符合逻辑的。

注意:建造者通常有一个「流式接口」,例如 PHPUnit 模拟生成器。

例子

  • PHPUnit: 模拟生成器

UML 图

file

代码

你也可以在 GitHub 上找到这个代码。

Director.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder;
  3. use DesignPatterns\Creational\Builder\Parts\Vehicle;
  4. /**
  5. * Director 类是建造者模式的一部分。 它可以实现建造者模式的接口
  6. * 并在构建器的帮助下构建一个复杂的对象
  7. *
  8. * 您也可以注入许多构建器而不是构建更复杂的对象
  9. */
  10. class Director
  11. {
  12. public function build(BuilderInterface $builder): Vehicle
  13. {
  14. $builder->createVehicle();
  15. $builder->addDoors();
  16. $builder->addEngine();
  17. $builder->addWheel();
  18. return $builder->getVehicle();
  19. }
  20. }

BuilderInterface.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder;
  3. use DesignPatterns\Creational\Builder\Parts\Vehicle;
  4. interface BuilderInterface
  5. {
  6. public function createVehicle();
  7. public function addWheel();
  8. public function addEngine();
  9. public function addDoors();
  10. public function getVehicle(): Vehicle;
  11. }

TruckBuilder.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder;
  3. use DesignPatterns\Creational\Builder\Parts\Vehicle;
  4. class TruckBuilder implements BuilderInterface
  5. {
  6. /**
  7. * @var Parts\Truck
  8. */
  9. private $truck;
  10. public function addDoors()
  11. {
  12. $this->truck->setPart('rightDoor', new Parts\Door());
  13. $this->truck->setPart('leftDoor', new Parts\Door());
  14. }
  15. public function addEngine()
  16. {
  17. $this->truck->setPart('truckEngine', new Parts\Engine());
  18. }
  19. public function addWheel()
  20. {
  21. $this->truck->setPart('wheel1', new Parts\Wheel());
  22. $this->truck->setPart('wheel2', new Parts\Wheel());
  23. $this->truck->setPart('wheel3', new Parts\Wheel());
  24. $this->truck->setPart('wheel4', new Parts\Wheel());
  25. $this->truck->setPart('wheel5', new Parts\Wheel());
  26. $this->truck->setPart('wheel6', new Parts\Wheel());
  27. }
  28. public function createVehicle()
  29. {
  30. $this->truck = new Parts\Truck();
  31. }
  32. public function getVehicle(): Vehicle
  33. {
  34. return $this->truck;
  35. }
  36. }

CarBuilder.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder;
  3. use DesignPatterns\Creational\Builder\Parts\Vehicle;
  4. class CarBuilder implements BuilderInterface
  5. {
  6. /**
  7. * @var Parts\Car
  8. */
  9. private $car;
  10. public function addDoors()
  11. {
  12. $this->car->setPart('rightDoor', new Parts\Door());
  13. $this->car->setPart('leftDoor', new Parts\Door());
  14. $this->car->setPart('trunkLid', new Parts\Door());
  15. }
  16. public function addEngine()
  17. {
  18. $this->car->setPart('engine', new Parts\Engine());
  19. }
  20. public function addWheel()
  21. {
  22. $this->car->setPart('wheelLF', new Parts\Wheel());
  23. $this->car->setPart('wheelRF', new Parts\Wheel());
  24. $this->car->setPart('wheelLR', new Parts\Wheel());
  25. $this->car->setPart('wheelRR', new Parts\Wheel());
  26. }
  27. public function createVehicle()
  28. {
  29. $this->car = new Parts\Car();
  30. }
  31. public function getVehicle(): Vehicle
  32. {
  33. return $this->car;
  34. }
  35. }

Parts/Vehicle.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder\Parts;
  3. abstract class Vehicle
  4. {
  5. /**
  6. * @var object[]
  7. */
  8. private $data = [];
  9. /**
  10. * @param string $key
  11. * @param object $value
  12. */
  13. public function setPart($key, $value)
  14. {
  15. $this->data[$key] = $value;
  16. }
  17. }

Parts/Truck.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder\Parts;
  3. class Truck extends Vehicle
  4. {
  5. }

Parts/Car.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder\Parts;
  3. class Car extends Vehicle
  4. {
  5. }

测试

Tests/DirectorTest.php

  1. <?php
  2. namespace DesignPatterns\Creational\Builder\Tests;
  3. use DesignPatterns\Creational\Builder\Parts\Car;
  4. use DesignPatterns\Creational\Builder\Parts\Truck;
  5. use DesignPatterns\Creational\Builder\TruckBuilder;
  6. use DesignPatterns\Creational\Builder\CarBuilder;
  7. use DesignPatterns\Creational\Builder\Director;
  8. use PHPUnit\Framework\TestCase;
  9. class DirectorTest extends TestCase
  10. {
  11. public function testCanBuildTruck()
  12. {
  13. $truckBuilder = new TruckBuilder();
  14. $newVehicle = (new Director())->build($truckBuilder);
  15. $this->assertInstanceOf(Truck::class, $newVehicle);
  16. }
  17. public function testCanBuildCar()
  18. {
  19. $carBuilder = new CarBuilder();
  20. $newVehicle = (new Director())->build($carBuilder);
  21. $this->assertInstanceOf(Car::class, $newVehicle);
  22. }
  23. }