0Pricing
PHP Academy · 课时

创建型模式:工厂、构建器、单例

使用创建型模式清晰地控制对象创建。

创建型模式:工厂、构建器、单例 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。

控制对象创建

创建型模式将您需要的内容与构建方式解耦。当构造过程具有条件性、包含多个步骤或必须集中管理时,在整个代码库中散布new会使调用方与具体类耦合。本节介绍工厂(以及工厂方法)、用于复杂组装的构建器,以及 Singleton(及其注意事项)。

简单工厂

简单工厂通过一个方法集中处理创建决策。调用方请求一种逻辑类型,便会获得一个已配置的具体实例,而无需知道类名。

<?php
interface Notifier { public function send(string $msg): string; }
final class EmailNotifier implements Notifier {
    public function send(string $m): string { return "email: $m"; }
}
final class SmsNotifier implements Notifier {
    public function send(string $m): string { return "sms: $m"; }
}

final class NotifierFactory {
    public static function make(string $channel): Notifier {
        return match ($channel) {
            'email' => new EmailNotifier(),
            'sms'   => new SmsNotifier(),
            default => throw new InvalidArgumentException($channel),
        };
    }
}
echo NotifierFactory::make('sms')->send('Hi'), PHP_EOL;

工厂方法

工厂方法模式将实例化过程推迟到子类。基类定义一个会调用抽象create()的算法;每个子类决定具体产品。这种方式符合 OCP:无需修改现有创建者即可添加新的创建者。

<?php
abstract class ReportExporter {
    abstract protected function create(): string;
    public function export(): string {
        return 'Exported as ' . $this->create();
    }
}
final class PdfExporter extends ReportExporter {
    protected function create(): string { return 'PDF'; }
}
final class CsvExporter extends ReportExporter {
    protected function create(): string { return 'CSV'; }
}
echo (new PdfExporter())->export(), PHP_EOL;
echo (new CsvExporter())->export(), PHP_EOL;

抽象工厂

抽象工厂用于生成必须配套使用的相关对象系列(例如,MySqlConnection与MySqlQueryBuilder)。由于同一个工厂会提供完整集合,因此可以保证各部分彼此兼容。当一个产品系列必须保持内部一致时,请使用它。

构建器:问题所在

当对象需要许多可选参数时,构造函数会膨胀成难以阅读的位置参数列表,或层层扩展的重载,而 PHP 甚至不支持这种重载。构建器模式会逐步组装复杂对象,并在最后返回一个完整且有效的产品。

流式构建器

流式构建器会让每个设置方法返回$this,然后由build()生成不可变结果。这样可以保持产品本身简单,并将组装逻辑转移到构建器中。

<?php
final class Query {
    public function __construct(public readonly string $sql) {}
}
final class QueryBuilder {
    private string $table = '';
    private array $where = [];
    public function from(string $t): self { $this->table = $t; return $this; }
    public function where(string $c): self { $this->where[] = $c; return $this; }
    public function build(): Query {
        $sql = "SELECT * FROM {$this->table}";
        if ($this->where) { $sql .= ' WHERE ' . implode(' AND ', $this->where); }
        return new Query($sql);
    }
}
echo (new QueryBuilder())->from('users')->where('age > 18')->build()->sql, PHP_EOL;

构建器与命名构造函数的比较

对于更简单的情况,PHP 8 的命名参数和命名构造函数(静态工厂方法)通常比完整的构建器更合适。

  • 当所有数据都能一次获得时,请使用命名参数。
  • 当组装过程包含多个步骤、具有条件性,或需要在多个调用位置复用时,请使用构建器。
<?php
final class Money {
    private function __construct(public readonly int $cents, public readonly string $cur) {}
    public static function fromDollars(float $d): self {
        return new self((int) round($d * 100), 'USD');
    }
    public static function zero(string $cur = 'USD'): self {
        return new self(0, $cur);
    }
}
var_dump(Money::fromDollars(19.99)->cents); // int(1999)

Singleton

Singleton保证只有一个实例,并提供一个全局访问点。请使用私有构造函数和静态访问器来实现它。对于确实必须在一个进程内唯一的事物,它确实有用,但它也是最容易被滥用的模式。

<?php
final class Config {
    private static ?self $instance = null;
    private array $data = ['debug' => true];
    private function __construct() {}
    public static function instance(): self {
        return self::$instance ??= new self();
    }
    public function get(string $k): mixed { return $this->data[$k] ?? null; }
}
var_dump(Config::instance()->get('debug'));
var_dump(Config::instance() === Config::instance()); // true

Singleton 为什么会带来问题

Singleton 会引入隐藏的全局状态:调用方通过Config::instance()直接获取依赖,而不是声明依赖,这会破坏可测试性和并行性。在现代 PHP 中,请优先在DI 容器中注册一个共享实例(“单一实例”生命周期),然后将其注入。这样既能获得一个实例,又不会产生全局耦合。

选择合适的工具

  • 简单工厂/工厂方法:通过创建决策隐藏具体类。
  • 抽象工厂:生成彼此兼容的产品系列。
  • 构建器:逐步组装复杂对象。
  • Singleton:最后的选择;优先使用由容器管理的共享服务。

这四种模式都能降低与具体构造函数的耦合,让代码更易于扩展和测试。

基于注册表的工厂

更动态的工厂会在注册表中查找创建者,因此新类型可以自行注册,无需修改工厂(真正符合 OCP)。插件系统和序列化器就是通过这种方式在运行时将类型名称解析为构造函数的。

<?php
interface Shape { public function name(): string; }
final class Circle implements Shape { public function name(): string { return 'circle'; } }
final class Square implements Shape { public function name(): string { return 'square'; } }

final class ShapeRegistry {
    private array $creators = [];
    public function register(string $key, callable $factory): void {
        $this->creators[$key] = $factory;
    }
    public function create(string $key): Shape {
        return ($this->creators[$key] ?? throw new InvalidArgumentException($key))();
    }
}
$r = new ShapeRegistry();
$r->register('circle', fn() => new Circle());
$r->register('square', fn() => new Square());
echo $r->create('square')->name(), PHP_EOL; // square

快速检查

请为以下场景选择最合适的创建型工具。

回顾

您使用创建型模式控制了对象创建:使用工厂隐藏具体类型并选择产品系列,使用构建器以流式方式组装复杂对象,并了解了 Singleton 及其权衡。现代开发的要点是:可以放心使用工厂和构建器,但应使用 DI 容器的共享实例生命周期替代 Singleton,从而让依赖关系保持明确且易于测试。

常见问题解答

「创建型模式:工厂、构建器、单例」课时是免费的吗?

是的 — 「创建型模式:工厂、构建器、单例」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。

「创建型模式:工厂、构建器、单例」这节课中我会学到什么?

使用创建型模式清晰地控制对象创建。 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 PHP Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「创建型模式:工厂、构建器、单例」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 PHP Academy 课中编写并运行代码吗?

能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 实践 SOLID 原则
  2. 创建型模式:工厂、构建器、单例
  3. 结构型模式:适配器、装饰器、外观
  4. 行为型模式:策略、观察者、命令
← 返回 PHP Academy