php设计模式(五):生成器模式(Builder)
- 陈大剩
- 2023-04-14 09:28:15
- 657
生成器模式
生成器模式又称为:建造者模式、Builder。生成是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你使用相同的创建代码生成不同类型和形式的对象。
使用于分步骤创建对象,使代码更加简约化。有需要分步骤进行的可以用生成器模式。
白话点说:比如创建一个 G 对象时,需要一些列过程,如先创建 A,后创建 B,最后再创建 C ,如果我们每次放到 G 的构造函数中去创建 A、B、C,这样麻烦又不省事,其实我们可以直接交给另一个类,让它按照顺序去创建 A、B、C,我们只负责调用这个类就行,这个类就叫做 生成器类。
问题
假设我们还是在开发一款组装 品牌台式电脑(华硕、联想、…)的应用,需要拼装一台能正常开机的台式电脑并计算出每台所有部件的价格。
我们可能会构建这样一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常在一个包含众多参数且让人基本看不懂的 构造函数 中; 甚至还有更糟糕的情况,那就是这些代码散落在客户端代码的多个位置。
// AsusComputer.php
class AsusComputer {
// 构造函数
public function __construct($mouse, $keyboard, $display,...)
{
...
}
}
如现在你组装的华硕电脑,需要在构造函数中加上诸多成员变量,有可能你传错或者忘记一个参数,你的电脑华硕电脑就运行不起来了,有没有解决这种繁琐的创建和参数修改呢?
解决方法
利用 生成器模式 将 对象构造代码从产品类中抽取出来,并将其放在一个名为生成器的独立对象中。生成器模式会将对象构造过程划分为一组步骤,每次创建对象时,你都需要通过生成器对象执行一系列步骤。无需 调用所有步骤,而只需调用创建特定对象配置所需的那些步骤即可。
STEP 1 声明抽象生成器基类
在基类生成器接口中声明这些步骤。
STEP 2 创建具体生成器类
为每个形式的产品创建具体生成器类, 并实现其构造步骤。
STEP 3 创建主管类
创建主管类。它可以使用同一生成器对象来封装多种构 造产品的方式。
STEP 4 客户端创建
客户端代码会同时创建生成器和主管对象。 构造开始前, 客户端必须将生成器对象传递给主管对象。 通常情况下, 客户端只需调用主管类构造函数一次即可。 主管类使用生成器对象完成后续所有制造任务。 还有另一种方式,那就是客户端 可以将生成器对象直接传递给主管类的制造方法。
结构
一个 interface 或者 abstract 生成器基类
多个实现 interface 或者继承 abstract 的具体产品生成器类
多个具体产品类
1个主管类,主管生成器(可有也可无,若无则在客户端生成)
代码示例
具体产品类
// AsusComputer.php
class AsusComputer
{
private array $parts = [];
/**
* 添加部件
* @param string $type
* @param float $price
* @return void
*/
public function add(string $type, float $price)
{
$this->parts[$type] = $price;
}
/**
* 显示部件价格
* @param string $type
* @return mixed
*/
public function show(string $type)
{
if (!isset($this->parts[$type])) {
throw new \InvalidArgumentException('没有此部件');
}
return $this->parts[$type];
}
}
生成器基类
// Builder.php
interface Builder
{
// 设置鼠标
public function setMouse();
// 设置键盘
public function setKeyboard();
// 设置显示器
public function setDisplay();
// 获取电脑
public function getResult();
}
具体产品生成器类
// AsusComputerBuilder.php
class AsusComputerBuilder implements Builder
{
public AsusComputer $asusComputer;
public function __construct()
{
$this->asusComputer = new AsusComputer();
}
// 设置鼠标以及价格
public function setMouse()
{
$this->asusComputer->add('mouse', 49.9);
}
// 设置键盘以及价格
public function setKeyboard()
{
$this->asusComputer->add('keyboard', 149.9);
}
// 设置显示器以及价格
public function setDisplay()
{
$this->asusComputer->add('display', 1490.9);
}
// 返回具体产品
public function getResult(): AsusComputer
{
return $this->asusComputer;
}
}
主管类
// Director.php
class Director
{
public function __construct(Builder $builder)
{
$builder->setMouse();
$builder->setKeyboard();
$builder->setDisplay();
}
}
客户端代码
// Client.php
$builder = new AsusComputerBuilder();
$director = new Director($builder);
var_dump($builder->getResult());
UML
代码
优缺点
优点
- 你可以分布创建对象,暂缓创建步骤或者递归运行创建步骤
- 生成不同形式的产品时, 你可以复用相同的制造代码。
- 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。
缺点
- 由于该模式需要新增多个类, 因此代码整体复杂程度会有所增加。