目的
创建一个复杂对象的一部分接口。
用处
如果一个类中有很多属性,为了避免构造函数的参数列表过长,影响代码的可读性和医用性,可以通过构造函数配合set()方法来解决。但是,如果存在下面情况的任意一种,我们就要考虑使用建造者模式了。
- 把类的必填属性放到构造函数中,强制创建对象的时候就设置。如果必填的属性有很多,把这些必填属性都放到构造函数中设置,那构造函数就又会出现参数列表很长的问题。如果我们把必填属性通过set()方法设置,那校验这些必填属性是否已经填写的逻辑就无处安放了。
- 如果类的属性之间有一定的依赖关系或者约束条件,我们继续使用构造函数配合set()方法的设计思路,那些依赖关系或约束条件的校验逻辑就无处安放了。
- 如果希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值,要实现这个功能,我们就不能在类中暴露set()方法。构造函数配合set()方法来设置属性值的方式就不适用了。
代码
<?php
namespace DesignPatterns\Creational\Builder\Parts;
class Car extends Vehicle
{
}
<?php
namespace DesignPatterns\Creational\Builder\Parts;
class Door
{
}
<?php
namespace DesignPatterns\Creational\Builder\Parts;
class Engine
{
}
<?php
namespace DesignPatterns\Creational\Builder\Parts;
class Truck extends Vehicle
{
}
<?php
namespace DesignPatterns\Creational\Builder\Parts;
abstract class Vehicle
{
protected $data = [];
public function setPart($key, $value)
{
$this->data[$key] = $value;
}
}
<?php
namespace DesignPatterns\Creational\Builder\Parts;
class Wheel
{
}
<?php
namespace DesignPatterns\Creational\Builder;
interface BuilderInterface
{
public function createVehicle();
public function addDoors();
public function addEngine();
public function addWheel();
public function getVehicle();
}
<?php
namespace DesignPatterns\Creational\Builder;
use DesignPatterns\Creational\Builder\Parts\Car;
use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Wheel;
class CarBuilder implements BuilderInterface
{
protected $car;
public function createVehicle()
{
$this->car = new Car();
}
public function addDoors()
{
$this->car->setPart('rightDoors', new Door());
$this->car->setPart('leftDoors', 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 getVehicle()
{
return $this->car;
}
}
<?php
namespace DesignPatterns\Creational\Builder;
use DesignPatterns\Creational\Builder\Parts\Door;
use DesignPatterns\Creational\Builder\Parts\Engine;
use DesignPatterns\Creational\Builder\Parts\Truck;
use DesignPatterns\Creational\Builder\Parts\Wheel;
class TruckBuilder implements BuilderInterface
{
private $truck;
public function createVehicle()
{
$this->truck = new Truck();
}
public function addDoors()
{
$this->truck->setPart('rightDoor', new Door());
$this->truck->setPart('leftDoor', new Door());
}
public function addEngine()
{
$this->truck->setPart('engine', new Engine());
}
public function addWheel()
{
$this->truck->setPart('rightWheel1', new Wheel());
$this->truck->setPart('rightWheel2', new Wheel());
$this->truck->setPart('rightWheel3', new Wheel());
$this->truck->setPart('leftWheel1', new Wheel());
$this->truck->setPart('leftWheel2', new Wheel());
$this->truck->setPart('leftWheel3', new Wheel());
}
public function getVehicle()
{
return $this->truck;
}
}
<?php
namespace DesignPatterns\Creational\Builder;
class Director
{
public function build(BuilderInterface $builder)
{
$builder->createVehicle();
$builder->addDoors();
$builder->addEngine();
$builder->addWheel();
return $builder->getVehicle();
}
}
<?php
namespace Tests;
use DesignPatterns\Creational\Builder\CarBuilder;
use DesignPatterns\Creational\Builder\Director;
use DesignPatterns\Creational\Builder\Parts\Car;
use DesignPatterns\Creational\Builder\Parts\Truck;
use DesignPatterns\Creational\Builder\TruckBuilder;
use PHPUnit\Framework\TestCase;
class BuilderTest extends TestCase
{
public function testCanBuilderCar()
{
$builder = new CarBuilder();
$director = new Director();
$vehicle = $director->build($builder);
$this->assertInstanceOf(Car::class, $vehicle);
}
public function testCanBuilderTruck()
{
$builder = new TruckBuilder();
$director = new Director();
$vehicle = $director->build($builder);
$this->assertInstanceOf(Truck::class, $vehicle);
}
}