命令模式解决请求逻辑动态组合、延迟执行与统一回滚问题,核心是分离“做什么”和“谁来做”,通过封装动作、持有接收者、暴露execute()实现,避免硬编码与if-else蔓延。

命令模式在 PHP 里到底要解决什么问题
不是为了炫技,而是当你有一堆请求逻辑(比如用户注册后发邮件、写日志、扣积分)需要动态组合、延迟执行、或统一回滚时,Command 能把“做什么”和“谁来做”彻底分开。否则容易写成一长串 if-else 或硬编码调用,改一个逻辑就得动好几处。
怎么写一个最小可用的 Command 类
核心就三点:封装动作、持有接收者、暴露 execute()。别急着抽象接口,先从具体命令开始。
常见错误现象:Call to undefined method execute() —— 忘了定义方法;或者把业务逻辑直接塞进 __construct(),导致无法延迟执行。
-
Command类不依赖具体业务类,只通过构造参数接收$receiver(比如UserService实例) - 所有参数应在
__construct()中传入并保存为属性,不在execute()里重新获取 - 避免在
execute()中做条件判断——那是调用方的事,命令只负责“干一件确定的事”
示例:
立即学习“PHP免费学习笔记(深入)”;
class SendWelcomeEmailCommand
{
private $userService;
private $userId;
public function __construct(UserService $userService, int $userId)
{
$this->userService = $userService;
$this->userId = $userId;
}
public function execute(): void
{
$user = $this->userService->findById($this->userId);
// ... 发邮件逻辑
}
}
调度器(Invoker)怎么安全地运行命令
调度器不是万能中转站,它只管调用 execute(),不关心命令内部怎么实现。重点是控制执行时机和上下文。
使用场景:队列消费、事务后钩子、后台任务分发。
- 不要在调度器里 new 命令实例——那又耦合了;应由上层(如 Controller)创建后传入
- 如果需要支持撤销,命令必须自带
undo(),且调度器要记录执行顺序(栈结构),但 PHP CLI 场景下多数不需要 - 注意异常处理:命令内抛出的异常应由调度器捕获并决定是否重试/跳过,而不是让整个流程崩掉
简短示例:
class CommandInvoker
{
public function run(CommandInterface $command): void
{
try {
$command->execute();
} catch (Exception $e) {
error_log('Command failed: ' . $e->getMessage());
throw $e; // 或静默吞掉,视场景而定
}
}
}
什么时候不该用命令模式
不是所有封装都叫命令模式。PHP 中大量滥用表现为:给单个函数包一层类,起名 SendEmailCommand,却没带来任何调度、组合、延迟或解耦价值。
- 纯同步、无状态、无复用需求的操作(比如简单写缓存),直接调函数更清晰
- 命令之间强依赖、必须按固定顺序执行,且无法拆分——这时候更适合状态机或流程引擎
- 项目已用 Laravel 的
Job或 Symfony 的Messenger,再手写命令模式反而增加维护成本
真正关键的复杂点在于:命令对象生命周期管理。比如命令持有了数据库连接或临时文件句柄,而调度器是长生命周期的(如 Swoole Worker),就容易引发资源泄漏或并发冲突——这点很容易被忽略。



















