行为型模式:策略、观察者、命令
使用行为型模式建模行为与通信。
行为型模式:策略、观察者、命令 是 CoddyKit 上的免费 PHP Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 PHP Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 PHP Academy 课程共包含 4 节课。
对行为建模
行为型模式负责对象如何协作以及如何分配职责。本节重点介绍三种在实际 PHP 系统中经常出现的模式:
- 策略——在运行时替换算法。
- Observer——向感兴趣的监听器广播事件。
- Command——将请求转换为可以排队、记录日志或撤销的对象。
策略:基本思路
策略模式在一个接口后定义一组可互换的算法,并允许客户端在运行时进行选择。它通过多态替代了下面臃肿的switch:上下文会将工作委托给它所持有的策略。
<?php
// The smell Strategy removes: branching on a type code
function price(string $type, float $base): float {
switch ($type) {
case 'standard': return $base + 5;
case 'express': return $base + 15;
default: throw new InvalidArgumentException($type);
}
}
echo price('express', 10), PHP_EOL; // 25
代码中的策略
这里的配送费用取决于所选承运商。每个策略封装一种定价算法;添加新的承运商时,无需修改上下文。
<?php
interface ShippingStrategy { public function cost(float $weight): float; }
final class Standard implements ShippingStrategy {
public function cost(float $w): float { return 5 + $w * 0.5; }
}
final class Express implements ShippingStrategy {
public function cost(float $w): float { return 15 + $w * 1.2; }
}
final class Cart {
public function __construct(private ShippingStrategy $strategy) {}
public function setStrategy(ShippingStrategy $s): void { $this->strategy = $s; }
public function quote(float $weight): float { return $this->strategy->cost($weight); }
}
$cart = new Cart(new Standard());
echo $cart->quote(10), PHP_EOL; // 10
$cart->setStrategy(new Express());
echo $cart->quote(10), PHP_EOL; // 27
使用闭包实现策略
在 PHP 中,策略不一定需要一个类。对于轻量级场景,callable或一等可调用语法完全可以作为有效的策略。当算法拥有自身的状态或依赖时,请使用完整的类;对于简单的无状态选择,请使用闭包。
<?php
$strategies = [
'asc' => fn(array $a) => sort($a) ? $a : $a,
'desc' => fn(array $a) => rsort($a) ? $a : $a,
];
$data = [3, 1, 2];
print_r($strategies['desc']($data)); // [3,2,1]
Observer:基本思路
Observer模式允许一个主题在状态发生变化时通知多个观察者,而无需知道它们是谁。这会将事件来源与其处理器解耦,也是事件系统、钩子和响应式更新的基础。
代码中的 Observer
主题会维护一个观察者列表,并对每个观察者调用update()。添加新的监听器无需修改主题的逻辑。
<?php
interface Observer { public function update(string $event): void; }
final class OrderSubject {
private array $observers = [];
public function subscribe(Observer $o): void { $this->observers[] = $o; }
public function placed(string $id): void {
foreach ($this->observers as $o) { $o->update("order $id placed"); }
}
}
final class EmailObserver implements Observer {
public function update(string $e): void { echo "email: $e" . PHP_EOL; }
}
final class AuditObserver implements Observer {
public function update(string $e): void { echo "audit: $e" . PHP_EOL; }
}
$subject = new OrderSubject();
$subject->subscribe(new EmailObserver());
$subject->subscribe(new AuditObserver());
$subject->placed('42');
Observer 与 PSR-14 的比较
现代 PHP 通常通过PSR-14 事件分发器来实现 Observer:分发器会将事件对象路由到已注册的监听器。其思维模型相同(主题 = 事件,观察者 = 监听器),但分发器能进一步解耦两者,并支持可停止事件。标准 PHP 库中存在 SplObserver/SplSubject,但如今很少使用。
Command:基本思路
Command模式将请求封装为一个对象,其中包含操作、接收者及其参数。由于请求现在是数据,您可以将其加入队列、记录日志、重试或撤销。最简单的命令只是将意图具体化为一个值,如下所示。
<?php
final class SendEmail {
public function __construct(
public readonly string $to,
public readonly string $subject
) {}
}
// The request is now data you can store, serialize, or enqueue
$cmd = new SendEmail('user@example.com', 'Welcome');
echo json_encode($cmd), PHP_EOL;
代码中的 Command
每个命令都实现execute()。调用者会持有并运行这些命令,而不必了解它们的具体行为。添加undo()后,就可以构建完整的撤销栈。
<?php
interface Command { public function execute(): string; }
final class Light { public bool $on = false; }
final class TurnOn implements Command {
public function __construct(private Light $light) {}
public function execute(): string { $this->light->on = true; return 'light on'; }
}
final class TurnOff implements Command {
public function __construct(private Light $light) {}
public function execute(): string { $this->light->on = false; return 'light off'; }
}
final class Invoker {
private array $queue = [];
public function add(Command $c): void { $this->queue[] = $c; }
public function run(): void {
foreach ($this->queue as $c) { echo $c->execute(), PHP_EOL; }
}
}
$light = new Light();
$inv = new Invoker();
$inv->add(new TurnOn($light));
$inv->add(new TurnOff($light));
$inv->run();
Command 总线
框架会基于此模式构建Command 总线:一个命令数据传输对象会被分发给唯一的处理器。总线周围的中间件可以统一添加事务、验证和日志记录。由于命令是普通的可序列化对象,同一个处理器既可以同步运行,也可以被推送到队列工作进程中。
在它们之间进行选择
- 策略:一个操作对应多个可互换的算法,由调用方选择。
- Observer:一个事件对应多个独立响应,来源无需知道处理器。
- Command:将请求具体化,使其能够排队、记录日志、重试或撤销。
它们可以很好地结合使用:命令处理器可以选择一种策略,并发出 Observer 事件。
快速检查
请将要求与相应的模式匹配。
回顾
您使用三种模式对行为进行了建模:使用策略在运行时替换算法(类或闭包),使用Observer向解耦的监听器广播状态变化(PSR-14 分发器的基础),以及使用Command将请求转换为可以排队、记录日志和撤销的对象(命令总线的基础)。它们共同让协作保持灵活,并使职责更加清晰。
常见问题解答
「行为型模式:策略、观察者、命令」课时是免费的吗?
是的 — 「行为型模式:策略、观察者、命令」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 PHP Academy 课程的其余内容,请升级到 CoddyKit PRO。 PHP Academy 课程共包含 4 节课。
「行为型模式:策略、观察者、命令」这节课中我会学到什么?
使用行为型模式建模行为与通信。 你通过在浏览器中直接运行的动手代码来练习 PHP Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 PHP Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 PHP Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「行为型模式:策略、观察者、命令」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 PHP Academy 课中编写并运行代码吗?
能。每节 PHP Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 实践 SOLID 原则
- 创建型模式:工厂、构建器、单例
- 结构型模式:适配器、装饰器、外观
- 行为型模式:策略、观察者、命令