- 示例工程
- 教程
【免费下载链接】DesignPatternsPHP
Sample code for several design patterns in PHP 8.x
导读
Fluent Interface(流式接口)是一种让代码读起来像自然语言句子一样清晰易懂的编程风格:每个方法调用完成后返回对象自身($this),从而把多次调用串联成一条流畅的链。本文以 DesignPatternsPHP 仓库中 Structural/FluentInterface 的Sql查询构建器为实战骨架,讲解该模式的核心机制、链式调用的返回值约定、__toString与Stringable接口的配合方式,并通过 PHPUnit 测试用例验证最终输出,读完即可在自己的 PHP 8.x 项目中落地这种 API 设计。
模式目的:让代码"读起来像句子"
Fluent Interface 的核心目的非常朴素:写出像自然语言(如英文)句子一样易于阅读的代码。传统写法把构造 SQL、配置对象、设置 Mock 期望的动作拆散在多行语句中,阅读者必须来回跳转;而链式写法让每次操作紧挨着上一次操作,语义顺序与阅读顺序一致,代码即文档。
实现 Fluent Interface 的机制要点只有一个——每个"配置型"方法都返回对象自身(return $this;)。这使得多个方法可以连续调用,而无需反复引用同一个变量。从源码结构看,Sql.php 中select、from、where三个方法全部遵循这一约定。
现实中的 Fluent Interface:Doctrine2 与 PHPUnit
仓库文档 README.rst 给出了两个广为人知的实例:
- Doctrine2 的 QueryBuilder:其工作方式与本文示例类几乎一致,例如
$qb->select('u')->from('User', 'u')->where('u.id = ?1')正是典型的链式查询构建。 - PHPUnit:使用流式接口构建 Mock 对象,例如
$mock->expects($this->once())->method('save')->with(...),通过连续调用描述期望行为。
这两个例子表明:凡是"一个对象需要在构造过程中反复配置多个维度"的场景(查询构建、对象装配、Mock 期望定义),都适合用 Fluent Interface 收敛 API 的阅读成本。
UML 结构:一句话说清角色分工
上图是仓库内置的 UML 类图。该模式结构极其精简,通常只有两类角色:
- 流式接口对象(如
Sql):持有内部状态(字段列表、表、条件),每个配置方法修改状态后返回$this; - 调用方(客户端/测试):通过链式调用组装出最终结果。
与许多结构性模式不同,Fluent Interface 不依赖抽象接口或继承体系,它更多是一种方法签名层面的约定:方法返回类型声明为自身类型(Sql),从而让 IDE 自动补全与静态分析能够沿着链继续提示下一批可用方法。
源码实现:Sql查询构建器逐方法拆解
完整实现见 Structural/FluentInterface/Sql.php,位于命名空间DesignPatterns\Structural\FluentInterface。类声明如下:
class Sql implements \Stringable实现 PHP 内置的Stringable接口(PHP 8.0+ 支持),意味着对象可以被(string)强制转换,这为"链式装配 + 一次性产出最终 SQL 字符串"提供了出口。
内部状态与select()
private array $fields = []; private array $from = []; private array $where = []; public function select(array $fields): Sql { $this->fields = $fields; return $this; }select接收字段名数组并整体覆盖内部$fields,返回$this以便继续链式调用。
from()与where():可累积的配置
public function from(string $table, string $alias): Sql { $this->from[] = $table . ' AS ' . $alias; return $this; } public function where(string $condition): Sql { $this->where[] = $condition; return $this; }与select的"覆盖"语义不同,from与where使用[]追加,支持多次调用累积多张表、多个条件——这正是 QueryBuilder 类库的真实行为模式。
__toString():把内部状态渲染成 SQL
public function __toString(): string { return sprintf( 'SELECT %s FROM %s WHERE %s', join(', ', $this->fields), join(', ', $this->from), join(' AND ', $this->where) ); }- 字段用
,连接,来自$fields; - 表以
表名 AS 别名形式用,连接,来自$from; - 条件用
AND连接,来自$where; - 最终统一格式化为
SELECT ... FROM ... WHERE ...一句完整 SQL。
由于类实现了\Stringable,(string) $query即可触发该渲染逻辑,无需额外提供build()之类的方法。
测试验证:链式调用的最终输出
仓库用 PHPUnit 用例锁定了链式 API 的契约,见 Structural/FluentInterface/Tests/FluentInterfaceTest.php:
public function testBuildSQL() { $query = (new Sql()) ->select(['foo', 'bar']) ->from('foobar', 'f') ->where('f.bar = ?'); $this->assertSame('SELECT foo, bar FROM foobar AS f WHERE f.bar = ?', (string) $query); }该测试验证了三点关键行为:
new Sql()后无需任何初始化即可直接开始链式调用;- 三个配置方法按序执行后,对象状态正确累积;
(string) $query产出与预期完全一致的 SQL 字符串,包括AS别名、逗号分隔的字段、AND连接的条件。
注意其中的f.bar = ?使用了占位符,暗示该 SQL 后续可交给 PDO 等预处理机制绑定参数——这是真实项目中推荐的安全写法。
在仓库中运行该示例与测试
DesignPatternsPHP 使用 Composer 管理依赖,PHP 版本要求为>=8.0(见 composer.json)。Stringable接口的使用正好契合这一版本下限。测试由 phpunit.xml.dist 统一调度,其中Structural/*/Tests目录下的*Test.php都会被自动发现。
运行验证步骤如下:
composer install # 安装 PHPUnit 等开发依赖 vendor/bin/phpunit --filter FluentInterfaceTest若输出 OK 且无失败断言,说明 Fluent Interface 的链式约定与__toString渲染逻辑在当前仓库代码上验证通过。此外,仓库的 Structural/README.md 将该模式归入结构性设计模式大类,可用于快速浏览它在整个模式体系中的位置。
小结与落地建议
结合 Sql.php 与 FluentInterfaceTest.php,可以提炼出在 PHP 8.x 项目中使用 Fluent Interface 的四条实践准则:
- 配置型方法一律
return $this,并把返回类型声明为自身类名,方便 IDE 沿链补全; - 明确区分"覆盖"与"追加"语义:如
select覆盖字段、from/where追加条目,并在文档中说明,避免调用方误判; - 配合
\Stringable或显式构建方法(如build())提供最终产物出口,让对象既能"攒配置"又能"出结果"; - 用单元测试锁定输出契约,如本例对整句 SQL 的
assertSame,防止后续改动破坏链式行为。
只要遵循"一次调用 = 一个句子成分"的直觉,Fluent Interface 就能把复杂对象的装配过程变成一段自解释的业务描述,这也是它在 ORM、Mock 框架等成熟 PHP 生态中长期占据一席之地的根本原因。
- 示例工程
- 教程
【免费下载链接】DesignPatternsPHP
Sample code for several design patterns in PHP 8.x
相关推荐
告别代码生硬输出:PHP Humanizer让数据呈现如自然语言般流畅
告别代码生硬输出:PHP Humanizer让数据呈现如自然语言般流畅 你是否还在为代码中冰冷的数字、日期和字符串输出而烦恼?用户看到"user_id"时皱眉,
开发工具后端终极指南:如何用DesignPatternsPHP的解释器模式实现简易自然语言处理
终极指南:如何用DesignPatternsPHP的解释器模式实现简易自然语言处理 在软件开发中,自然语言处理(NLP)常常被视为复杂且高深的领域。但借助 De
示例工程教程Webmozart Assert 实战指南:用 webmozart/assert 写出安全、易读的 PHP 输入校验代码
Webmozart Assert 实战指南:用 webmozart/assert 写出安全、易读的 PHP 输入校验代码 Webmozart Assert( w
开发工具代码质量
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考