以下是一个 PHP 中观察者模式(Observer Pattern) 的具体应用示例,结合电商系统的场景,展示如何通过观察者模式实现对象间的松耦合通信。

🛒 一、场景:电商系统中的库存更新通知

假设我们有一个电商平台,当某个商品的库存发生变化时,需要通知以下系统模块:

  • 客服系统:提醒客服库存不足;
  • 邮件系统:向用户发送库存恢复通知;
  • 短信系统:向用户发送库存恢复通知。

通过观察者模式,我们可以将这些模块解耦,使得商品库存的更新逻辑与通知逻辑分离。

🧱 二、代码实现(PHP)

1. 定义 Subject 接口和 Observer 接口

<?php

// 主题接口(被观察者)
interface Subject {
    public function attach(Observer $observer): void; // 注册观察者
    public function detach(Observer $observer): void; // 移除观察者
    public function notify(): void; // 通知所有观察者
}

// 观察者接口
interface Observer {
    public function update(Subject $subject): void; // 更新逻辑
}

2. 实现具体主题类(商品库存)

<?php

// 商品库存类(具体主题)
class Product implements Subject {
    private array $observers = []; // 观察者列表
    private int $stock = 0; // 当前库存

    // 注册观察者
    public function attach(Observer $observer): void {
        $this->observers[] = $observer;
    }

    // 移除观察者
    public function detach(Observer $observer): void {
        $key = array_search($observer, $this->observers, true);
        if ($key !== false) {
            unset($this->observers[$key]);
        }
    }

    // 通知所有观察者
    public function notify(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    // 修改库存并触发通知
    public function setStock(int $newStock): void {
        $this->stock = $newStock;
        $this->notify(); // 库存变化后通知观察者
    }

    // 获取当前库存
    public function getStock(): int {
        return $this->stock;
    }
}

3. 实现具体观察者类

3.1客服系统观察者
<?php

class CustomerServiceObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof Product && $subject->getStock() < 10) {
            echo "客服系统通知:商品库存不足(当前库存:" . $subject->getStock() . ")\n";
        }
    }
}
3.2 邮件系统观察者
<?php

class EmailObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof Product && $subject->getStock() > 0) {
            echo "邮件系统通知:商品库存已恢复(当前库存:" . $subject->getStock() . ")\n";
        }
    }
}
3.3 短信系统观察者
<?php

class SMSObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof Product && $subject->getStock() > 0) {
            echo "短信系统通知:商品库存已恢复(当前库存:" . $subject->getStock() . ")\n";
        }
    }
}

3.4. 客户端调用示例

<?php

// 创建商品对象
$product = new Product();

// 创建观察者对象
$customerService = new CustomerServiceObserver();
$emailService = new EmailObserver();
$smsService = new SMSObserver();

// 注册观察者
$product->attach($customerService);
$product->attach($emailService);
$product->attach($smsService);

// 模拟库存变化
echo "初始库存:5\n";
$product->setStock(5); // 触发客服系统的库存不足通知

echo "\n库存更新:15\n";
$product->setStock(15); // 触发邮件和短信系统的库存恢复通知

echo "\n库存更新:8\n";
$product->setStock(8); // 再次触发客服系统的库存不足通知

✅ 三、输出结果

初始库存:5
客服系统通知:商品库存不足(当前库存:5)

库存更新:15
邮件系统通知:商品库存已恢复(当前库存:15)
短信系统通知:商品库存已恢复(当前库存:15)

库存更新:8
客服系统通知:商品库存不足(当前库存:8)

🌟 四、优势与适用场景

4.1 优势

  1. 解耦:商品库存逻辑与通知逻辑完全分离,新增或移除观察者时无需修改主题类;
  2. 灵活性:可以动态注册/注销观察者,适应不同业务需求;
  3. 扩展性:新增观察者(如微信通知、App推送)只需添加新类,无需修改现有代码;
  4. 符合开闭原则:对扩展开放,对修改关闭。

4.2 适用场景

  • 事件驱动系统:如用户注册后触发邮件、短信通知;
  • 状态监控系统:如服务器监控、库存监控;
  • GUI 界面更新:如数据变化后自动刷新视图;
  • 消息队列:如异步任务触发多个消费者。

⚠️ 五、注意事项

  1. 性能问题:如果有大量观察者,频繁调用 notify() 可能会影响性能;
  2. 循环引用:避免观察者与主题之间的循环依赖,可能导致内存泄漏;
  3. 状态传递:观察者通过 update() 方法获取主题状态时,需确保状态的准确性。

🧩 六、PHP 内置支持(SplSubject)

PHP 提供了内置的观察者接口 SplSubjectSplObserver,可以直接使用:

use SplSubject;
use SplObserver;

class Product implements SplSubject {
    // 实现 attach(), detach(), notify() 方法
}

class EmailObserver implements SplObserver {
    public function update(SplSubject $subject) {
        // 处理逻辑
    }
}

🧾 七、总结

观察者模式是一种经典的行为型设计模式,适用于一对多依赖关系的场景。通过将观察者与主题解耦,可以显著提升系统的灵活性和可维护性。在 PHP 项目中,观察者模式常用于事件驱动系统、消息通知系统等场景。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐