观察者模式
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 73 课,共 91 课。
观察者模式是一种行为型设计模式,用于建立对象之间的一对多关系。当一个对象(主题)的状态发生变化时,它的所有依赖对象(观察者)都会自动收到通知并更新。
想想订阅新闻简报的场景。新闻简报是主题,订阅者是观察者。发布新一期时,所有订阅者都会自动收到它,新闻简报不需要知道每位订阅者的详细信息。
这是一个用 PHP 编写的基础实现:
<?php
interface Observer {
public function update(string $message): void;
}
class Newsletter {
private array $subscribers = [];
public function subscribe(Observer $observer): void {
$this->subscribers[] = $observer;
}
public function publish(string $content): void {
foreach ($this->subscribers as $subscriber) {
$subscriber->update($content);
}
}
}
class EmailSubscriber implements Observer {
public function __construct(private string $email) {}
public function update(string $message): void {
echo "Sending to {$this->email}: $message\n";
}
}
$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");
输出:
Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!这种模式将主题与观察者解耦。Newsletter 类不需要知道它通知的是电子邮件订阅者、SMS 订阅者,还是推送通知服务。它只需对任何实现了 Observer 接口的对象调用 update()。这样一来,无需修改现有代码,就能轻松添加新的观察者类型。
挑战
简单让我们使用观察者模式构建一个股票价格提醒系统。当股票价格发生变化时,所有已注册的投资者都会自动收到通知。这是这种模式在现实中的一个绝佳应用。
你会将代码分成 4 个文件:
Observer.php:定义一个Observer接口,其中只有一个方法update(string $stockName, float $price): void。所有观察者都必须遵循这份约定,才能接收通知。Investors.php:引入 Observer 文件,并创建两个实现该接口的类:EmailInvestor:在构造函数中接收一个电子邮件地址(使用构造函数属性提升)。它的update()方法会打印"Email to [email]: [stockName] is now $[price]"SmsInvestor:在构造函数中接收一个电话号码。它的update()方法会打印"SMS to [phone]: [stockName] is now $[price]"
Stock.php:引入 Observer 文件,并创建一个充当主题的Stock类。你的股票类应该:- 存储股票名称和当前价格(名称使用构造函数属性提升,价格初始化为
0.0) - 维护一个私有的观察者数组
- 包含一个用于添加观察者的
subscribe(Observer $observer): void方法 - 包含一个
setPrice(float $price): void方法,用于更新价格并通知所有观察者
update()方法,并传入股票名称和新价格。- 存储股票名称和当前价格(名称使用构造函数属性提升,价格初始化为
main.php:引入 Investors 和 Stock 文件。你会收到 3 项输入:股票名称、电子邮件地址和电话号码。使用给定的名称创建一个
Stock。订阅一个使用该电子邮件地址的EmailInvestor,以及一个使用该电话号码的SmsInvestor。将股票价格设为
150.50,以触发向所有订阅者发送通知。
观察者模式让你的 Stock 类与具体的通知方式解耦。之后,你可以轻松添加 PushInvestor 或 SlackInvestor,而完全不必修改 Stock 类。
亲手试试
<?php
require_once 'Investors.php';
require_once 'Stock.php';
// 读取输入
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));
// TODO:用给定的名称创建一个 Stock 对象
// TODO:用邮箱地址订阅一个 EmailInvestor
// TODO:用电话号码订阅一个 SmsInvestor
// TODO:将股票价格设置为 150.50,以触发通知
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器