Menu
CoddyTech

观察者模式

本课属于 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()。这样一来,无需修改现有代码,就能轻松添加新的观察者类型。

challenge icon

挑战

简单

让我们使用观察者模式构建一个股票价格提醒系统。当股票价格发生变化时,所有已注册的投资者都会自动收到通知。这是这种模式在现实中的一个绝佳应用。

你会将代码分成 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,以触发通知
?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器