建造者模式(Builder)
前言
建造者模式用于分步骤创建复杂对象,将对象的构建过程与其表示分离。当你需要创建一个包含多个可选参数的复杂对象时,建造者模式是最优选择。本文将详细讲解其核心原理、实现方式及链式调用技巧。
一、核心概念
1.1 定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1.2 核心角色
| 角色 | 说明 |
|---|---|
| 产品(Product) | 被构建的复杂对象 |
| 抽象建造者(Builder) | 定义构建产品各部件的抽象接口 |
| 具体建造者(ConcreteBuilder) | 实现构建接口,组装产品 |
| 指挥者(Director) | 控制构建过程,按顺序调用建造者方法 |
二、代码实现
2.1 经典实现
php
<?php
// 产品类
class Product
{
private array $parts = [];
public function addPart(string $part): void
{
$this->parts[] = $part;
}
public function listParts(): void
{
echo "Product parts: " . implode(", ", $this->parts) . PHP_EOL;
}
}
// 抽象建造者
interface Builder
{
public function buildPartA(): void;
public function buildPartB(): void;
public function buildPartC(): void;
public function getResult(): Product;
public function reset(): void;
}
// 具体建造者
class ConcreteBuilder implements Builder
{
private Product $product;
public function __construct()
{
$this->product = new Product();
}
public function reset(): void
{
$this->product = new Product();
}
public function buildPartA(): void
{
$this->product->addPart("Part A");
}
public function buildPartB(): void
{
$this->product->addPart("Part B");
}
public function buildPartC(): void
{
$this->product->addPart("Part C");
}
public function getResult(): Product
{
$result = $this->product;
$this->reset();
return $result;
}
}
// 指挥者
class Director
{
private Builder $builder;
public function __construct(Builder $builder)
{
$this->builder = $builder;
}
public function setBuilder(Builder $builder): void
{
$this->builder = $builder;
}
// 构建最简产品
public function buildMinimalProduct(): void
{
$this->builder->buildPartA();
}
// 构建完整产品
public function buildFullProduct(): void
{
$this->builder->buildPartA();
$this->builder->buildPartB();
$this->builder->buildPartC();
}
// 自定义构建
public function buildCustomProduct(): void
{
$this->builder->buildPartB();
$this->builder->buildPartC();
}
}
// 使用
$builder = new ConcreteBuilder();
$director = new Director($builder);
$director->buildFullProduct();
$product = $builder->getResult();
$product->listParts();
// Product parts: Part A, Part B, Part C2.2 链式调用实现
php
<?php
// 产品类
class Pizza
{
public string $dough = "";
public string $sauce = "";
public string $topping = "";
public string $size = "";
public function describe(): string
{
return "Pizza: {$this->size}, {$this->dough}, {$this->sauce}, {$this->topping}";
}
}
// 链式建造者
class PizzaBuilder
{
private Pizza $pizza;
public function __construct()
{
$this->pizza = new Pizza();
}
public function setSize(string $size): self
{
$this->pizza->size = $size;
return $this;
}
public function setDough(string $dough): self
{
$this->pizza->dough = $dough;
return $this;
}
public function setSauce(string $sauce): self
{
$this->pizza->sauce = $sauce;
return $this;
}
public function setTopping(string $topping): self
{
$this->pizza->topping = $topping;
return $this;
}
public function build(): Pizza
{
return $this->pizza;
}
}
// 使用链式调用
$pizza = (new PizzaBuilder())
->setSize("Large")
->setDough("Thin Crust")
->setSauce("Tomato")
->setTopping("Cheese")
->build();
echo $pizza->describe() . PHP_EOL;
// Pizza: Large, Thin Crust, Tomato, Cheese2.3 实际案例:HTTP 请求构建器
php
<?php
class HttpRequest
{
public string $url = "";
public string $method = "GET";
public array $headers = [];
public ?array $body = null;
public int $timeout = 5000;
public function send(): void
{
echo "Sending {$this->method} request to {$this->url}" . PHP_EOL;
// 实际场景使用 cURL 或 Guzzle 发送请求
$ch = curl_init($this->url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method);
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->buildHeaderLines());
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
if ($this->body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($this->body));
}
curl_exec($ch);
curl_close($ch);
}
private function buildHeaderLines(): array
{
$lines = [];
foreach ($this->headers as $key => $value) {
$lines[] = "{$key}: {$value}";
}
return $lines;
}
}
class HttpRequestBuilder
{
private HttpRequest $request;
public function __construct(string $url)
{
$this->request = new HttpRequest();
$this->request->url = $url;
}
public function method(string $m): self
{
$this->request->method = $m;
return $this;
}
public function header(string $key, string $value): self
{
$this->request->headers[$key] = $value;
return $this;
}
public function json(array $body): self
{
$this->request->body = $body;
$this->header("Content-Type", "application/json");
return $this;
}
public function timeout(int $ms): self
{
$this->request->timeout = $ms;
return $this;
}
public function build(): HttpRequest
{
return $this->request;
}
}
// 使用
$request = (new HttpRequestBuilder("https://api.example.com/users"))
->method("POST")
->header("Authorization", "Bearer token123")
->json(["name" => "Flynn", "email" => "flynn@example.com"])
->timeout(10000)
->build();
$request->send();三、适用场景
| 场景 | 说明 |
|---|---|
| 对象参数多且可选 | 避免构造函数参数爆炸 |
| 分步骤创建对象 | 需要控制创建顺序 |
| 同一构建过程不同表示 | 相同步骤创建不同产品 |
| 不可变对象创建 | 构建完成后对象不可变 |
| 配置对象 | 复杂配置项分步设置 |
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 分步构建复杂对象 | 需要创建多个类 |
| 相同构建过程不同表示 | 代码量增加 |
| 精确控制构建过程 | 简单对象过度设计 |
| 符合单一职责原则 | 建造者需要了解产品结构 |
| 链式调用提升可读性 | 指挥者与建造者耦合 |
五、常见踩坑与问题排查
5.1 构造函数参数爆炸
php
<?php
// 问题:参数过多,可读性差
class BadUser
{
public function __construct(
string $name,
int $age,
string $email,
string $phone,
string $address,
string $city,
string $country,
string $zip
) {
// ...
}
}
// 使用建造者模式解决
class User
{
public string $name = "";
public int $age = 0;
public string $email = "";
}
class UserBuilder
{
private User $user;
public function __construct()
{
$this->user = new User();
}
public function setName(string $name): self
{
$this->user->name = $name;
return $this;
}
public function setAge(int $age): self
{
$this->user->age = $age;
return $this;
}
public function build(): User
{
return $this->user;
}
}5.2 构建未完成就调用 build()
php
<?php
// 问题:产品部分属性未设置
$pizza = (new PizzaBuilder())
->setSize('Large')
->build();
// pizza->dough 为空字符串
// 解决:在 build() 中添加验证
public function build(): Pizza
{
if ($this->pizza->dough === '') {
throw new \RuntimeException('Dough is required');
}
return $this->pizza;
}六、优化方案与进阶
6.1 关联数组配置版建造者
php
<?php
class Pizza
{
private array $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function describe(): string
{
$size = $this->config['size'] ?? '';
$dough = $this->config['dough'] ?? '';
$sauce = $this->config['sauce'] ?? '';
$topping = $this->config['topping'] ?? '';
return "Pizza: {$size}, {$dough}, {$sauce}, {$topping}";
}
}
// 使用关联数组替代链式调用
$pizza = new Pizza([
'size' => 'Large',
'dough' => 'Thin Crust',
'sauce' => 'Tomato',
'topping' => 'Cheese',
]);6.2 结合不可变对象
php
<?php
class ImmutablePizza
{
public readonly string $size;
public readonly string $dough;
public readonly string $sauce;
private function __construct(PizzaBuilder $builder)
{
$this->size = $builder->size;
$this->dough = $builder->dough;
$this->sauce = $builder->sauce;
}
public static function builder(): PizzaBuilder
{
return new PizzaBuilder();
}
}
class PizzaBuilder
{
public string $size = "";
public string $dough = "";
public string $sauce = "";
public function setSize(string $size): self
{
$this->size = $size;
return $this;
}
public function setDough(string $dough): self
{
$this->dough = $dough;
return $this;
}
public function build(): ImmutablePizza
{
return new ImmutablePizza($this);
}
}
// 使用(PHP 8.1+ 支持 readonly 属性)
$pizza = ImmutablePizza::builder()
->setSize('Large')
->setDough('Thin Crust')
->build();七、全文总结
建造者模式的核心是 将复杂对象的构建过程与表示分离,支持分步创建和链式调用。
核心要点:
- 适用于参数多、创建过程复杂的对象
- 链式调用提升代码可读性和流畅性
- 指挥者控制构建流程,建造者执行具体构建
- 可与不可变对象结合,创建线程安全产品
- 简单对象不需要建造者,避免过度设计
