Skip to content

建造者模式(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 C

2.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, Cheese

2.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();

七、全文总结

建造者模式的核心是 将复杂对象的构建过程与表示分离,支持分步创建和链式调用

核心要点

  1. 适用于参数多、创建过程复杂的对象
  2. 链式调用提升代码可读性和流畅性
  3. 指挥者控制构建流程,建造者执行具体构建
  4. 可与不可变对象结合,创建线程安全产品
  5. 简单对象不需要建造者,避免过度设计