建造者模式也称生成器模式,核心思想是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式
在建造者模式中,有4个角色
抽象建造者(Builder)
定义一个抽象接口,规范产品各个组成成分的建造(即规范具体建造者的方法实现)。其中所规范的方法中必须包括建造方法和结果返回方法
具体建造者(ConcreteBuilder)
实现抽象建造者角色所定义的方法。具体建造者与业务逻辑关联性较大,应用程序最终会通过调用此角色中所实现的建造方法按照业务逻辑创建产品,在建造完成后通过结果返回方法返回建造的产品实例。一般在外部由客户或一个抽象工厂创建
产品(Product)
在指导者的指导下由建造者所创建的那个复杂的对象
导演者角色与客户端直接打交道,它理解客户端的业务逻辑,将客户端创建产品的请求拆分成对产品组成部分的请求,然后调用具体产品角色执行建造操作。它分离了客户端与具体建造者
导演者(Director)
此角色的作用是调用具体的建造者角色建造产品。导演者与产品类没有直接关系,与产品类交谈的是具体抽象角色
建造者模式和抽象工厂模式很像,总体上,建造者模式仅仅只比抽象工厂模式多了一个“导演类”的角色。与抽象工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类 —— 导演类。也就是说,抽像工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将完整建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端
两者都是创建型设计模式,但还是有一定的区别
在工厂方法模式里,我们关注的是一个产品整体,如电脑整体,无须关心产品的各部分是如何创建出来的;但在建造者模式中,一个具体产品的产生是依赖各个部件的产生以及装配顺序,它关注的是“由零件一步一步地组装出产品对象”。简单地说,工厂模式是一个对象创建的粗线条应用,建造者模式则是通过细线条勾勒出一个复杂对象,关注的是产品组成部分的创建过程。
工厂方法模式创建的产品一般都是单一性质产品,如电脑,都是一个模样,而建造者模式创建的则是一个复合产品,它由各个部件复合而成,部件不同产品对象当然不同。这不是说工厂方法模式创建的对象简单,而是指它们的粒度大小不同。一般来说,工厂方法模式的对象粒度比较粗,建造者模式的产品对象粒度比较细
现在的需求是 你开了一家电脑组装店 有客人来店里想要组装电脑
电脑组装分为四大件 CPU、主板、内存、显卡
每一种配件又有各自的品牌,暂定有intel和mac两个品牌
组装电脑有高配置和低配置的区别
高配置可能用intel的CPU mac的显卡
低配置可能用intel的CPU 不要显卡 使用主板上的集成显卡
现在我们考虑一下实现
使用工厂模式,工厂模式和抽象工厂模式都是专注于产品,对象的创建过程全部封装在工厂类里,那么,针对于每一种配置,我都要有相对应的工厂
高配置 intel CPU mac 显卡 mac 主板 mac 内存
高配置 intel CPU mac 显卡 intel 主板 mac 内存
低配置 intel CPU intel 主板 intel 内存
针对于这三种情况,我就需要有三个工厂分别来生产对应配置的电脑,真实情况下,电脑的配件更多,有硬盘、散热器、机箱等,品牌更是多如牛毛
这种情况下就不再适合用工厂模式了,我们看一下建造者模式的描述:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式
现在使用建造者模式来实现这个需求
抽象建造者
<?php
/**
* 建造者接口
* User: tiansi
* Date: 18/2/1
* Time: 下午5:11
* $brand 品牌
*/
interface IBuilderInterface
{
public function createComputer();
public function addCpu($brand);
public function addXianka($brand);
public function addZhuban($brand);
public function addNeicun($brand);
}
具体建造者
<?php
/**
* 电脑建造者类
* User: tiansi
* Date: 18/2/1
* Time: 下午5:20
*/
class ComputerBuilder implements IBuilderInterface
{
protected $_computer;
function __construct()
{
$this->_computer = new Computer();
}
public function createComputer()
{
return $this->_computer;
}
public function addCpu($brand)
{
$this->_computer->cpu = $brand;
}
public function addXianka($brand)
{
$this->_computer->neicun = $brand;
}
public function addZhuban($brand)
{
$this->_computer->xianka = $brand;
}
public function addNeicun($brand)
{
$this->_computer->zhuban = $brand;
}
}
具体产品
<?php
/**
* 电脑产品类
* Created by PhpStorm.
* User: tiansi
* Date: 18/2/1
* Time: 下午5:25
*/
class Computer
{
public $cpu;
public $xianka;
public $neicun;
public $zhuban;
}
导演者
具体的操作流程类 所以叫导演
<?php
/**
* 导演类
* User: tiansi
* Date: 18/2/1
* Time: 下午5:37
*/
include 'ComputerBuilder.php';
class Director
{
public function builderComputerOne(ComputerBuilder $builder)
{
$builder->addCpu('intel');
$builder->addNeicun('mac');
$builder->addXianka('mac');
$builder->addZhuban('mac');
return $builder->createComputer();
}
public function buildComputerTwo(ComputerBuilder $builder)
{
$builder->addCpu('intel');
$builder->addNeicun('mac');
$builder->addXianka('mac');
$builder->addZhuban('intel');
return $builder->createComputer();
}
public function buildComputerThree(ComputerBuilder $builder)
{
$builder->addCpu('intel');
$builder->addNeicun('intel');
$builder->addZhuban('intel');
return $builder->createComputer();
}
}
客户端调用
$director = new Director();
$computerBuilder = new ComputerBuilder();
$computerOne = $director->builderComputerOne($computerBuilder);
var_dump($computerOne);
//输出结果
class Computer#3 (4) {
public $cpu =>
string(5) "intel"
public $xianka =>
string(3) "mac"
public $neicun =>
string(3) "mac"
public $zhuban =>
string(3) "mac"
}
需要别的配置,只需要在导演类增加一个配置方法即可
优点:
在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。