设计模式[创建型]06--建造者模式(Builder)

一、简介

无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式,这就是建造者模式的模式动机。

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。根据中文翻译的不同,建造者模式又可以称为生成器模式。

二、场景

比如:去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。

使用场景:
1)需要生成的对象具有复杂的内部结构。
2)需要生成的对象内部属性本身相互依赖。

建造者模式主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

三、类结构

建造者模式包含如下角色:

角色 简述
Builder 抽象建造者
ConcreteBuilder 具体建造者
Director 指挥者
Product 产品角色
  • 抽象建造者定义了产品的创建方法和返回方法。
  • 指挥者的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。
  • 客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象

四、UML图

五、代码分析

下面以建造汽车为例。

1、抽象建造者

interface BuilderInterface
{
    public function createVehicle();

    public function addWheel();

    public function addEngine();

    public function addDoors();

    public function getVehicle();
}

2、具体建造者

// 建造大卡车
class TruckBuilder implements BuilderInterface
{
    /**
     * @var Truck
     */
    private $truck;

    public function createVehicle(){
        $this->truck = new Truck();
    }

    public function addDoors(){
        $this->truck->setPart('rightDoor', new Door());
        $this->truck->setPart('leftDoor', new Door());
        $this->truck->setPart('trunkLid', 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 getVehicle(){
        return $this->truck;
    }
}

// 建造小汽车
class CarBuilder implements BuilderInterface
{
    /**
     * @var Car
     */
    private $car;

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

    public function addDoors(){
        $this->car->setPart('rightDoor', new Door());
        $this->car->setPart('leftDoor', 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;
    }
}

3、指挥者

/**
 * Director 类是建造者模式的一部分。 它可以实现建造者模式的接口
 * 并在构建器的帮助下构建一个复杂的对象
 *
 * 您也可以注入许多构建器而不是构建更复杂的对象
 */
class Director
{
    public function build(BuilderInterface $builder)
    {
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

4、产品角色

// 抽象产品
abstract class Vehicle
{
    /**
     * @var object[]
     */
    private $data = [];

    /**
     * @param string $key
     * @param object $value
     */
    public function setPart($key, $value){
        $this->data[$key] = $value;
    }
}

class Truck extends Vehicle{
}
class Car extends Vehicle{
}

// 产品部件
class Engine{
}
class Wheel{
}
class Door{
}

5、使用案例

$diretor = new Director();
$myTruck = $diretor->build(new TruckBuilder());
$myCar = $diretor->build(new CarBuilder());

var_dump($myTruck);
var_dump($myCar);

输出结果:

object(Truck)#3 (1) {
  ["data":"Vehicle":private]=>
  array(10) {
    ["rightDoor"]=>
    object(Door)#4 (0) {
    }
    ["leftDoor"]=>
    object(Door)#5 (0) {
    }
    ["trunkLid"]=>
    object(Door)#6 (0) {
    }
    ["truckEngine"]=>
    object(Engine)#7 (0) {
    }
    ["wheel1"]=>
    object(Wheel)#8 (0) {
    }
    ["wheel2"]=>
    object(Wheel)#9 (0) {
    }
    ["wheel3"]=>
    object(Wheel)#10 (0) {
    }
    ["wheel4"]=>
    object(Wheel)#11 (0) {
    }
    ["wheel5"]=>
    object(Wheel)#12 (0) {
    }
    ["wheel6"]=>
    object(Wheel)#13 (0) {
    }
  }
}
object(Car)#14 (1) {
  ["data":"Vehicle":private]=>
  array(7) {
    ["rightDoor"]=>
    object(Door)#15 (0) {
    }
    ["leftDoor"]=>
    object(Door)#16 (0) {
    }
    ["engine"]=>
    object(Engine)#17 (0) {
    }
    ["wheelLF"]=>
    object(Wheel)#18 (0) {
    }
    ["wheelRF"]=>
    object(Wheel)#19 (0) {
    }
    ["wheelLR"]=>
    object(Wheel)#20 (0) {
    }
    ["wheelRR"]=>
    object(Wheel)#21 (0) {
    }
  }
}

六、特点

1、优点:

  • 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。

2、缺点:

  • 产品必须有共同点,范围有限制。
  • 如内部变化复杂,会有很多的建造类。

3、注意事项:
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

推荐阅读更多精彩内容