首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 設計模式實戰:觀察者模式完整實現與事件驅動應用

     編輯:PHP 相關知識

觀察者模式定義對象間一對多依賴,主題狀態變更時自動通知所有觀察者。本文提供標準 PHP 實現,修正原代碼拼寫錯誤,並擴展事件分發器實戰版本。

模式角色結構

  1. Subject(抽象主題):管理觀察者集合,提供 attach/detach/notify
  2. Observer(抽象觀察者):聲明 update() 方法
  3. ConcreteSubject(具體主題):持有業務狀態,變更時調用 notify()
  4. ConcreteObserver(具體觀察者):實現 update() 響應狀態變更

標準修正版實現

<?php
// 1. 抽象主題
interface Subject {
    public function attach(Observer $observer): void;
    public function detach(Observer $observer): void;
    public function notify(): void;
}

// 2. 具體主題
class ConcreteSubject implements Subject {
    private array $observers = [];
    private string $state = '';

    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 setState(string $state): void {
        $this->state = $state;
        $this->notify();
    }
    public function getState(): string { return $this->state; }
}

// 3. 抽象觀察者
interface Observer {
    public function update(Subject $subject): void;
}

// 4. 具體觀察者
class ConcreteObserver implements Observer {
    private string $name;
    public function __construct(string $name) { $this->name = $name; }

    public function update(Subject $subject): void {
        if ($subject instanceof ConcreteSubject) {
            echo "觀察者 [{$this->name}] 收到通知:主題狀態變更為 '{$subject->getState()}'
"; } } } // 客戶端測試 $subject = new ConcreteSubject(); $obs1 = new ConcreteObserver('日志服務'); $subject->attach($obs1); $subject->setState('訂單創建'); // 通知 obs1 $obs2 = new ConcreteObserver('郵件服務'); $subject->attach($obs2); $subject->setState('訂單付款'); // 通知 obs1, obs2 $subject->detach($obs1); $subject->setState('訂單發貨'); // 僅通知 obs2 ?>

進階實戰:事件分發器(支持優先級、通配符、一次性監聽)

<?php
class EventDispatcher {
    private array $listeners = []; // [event => [priority => [listener, isOnce]]]

    public function on(string $event, callable $listener, int $priority = 0, bool $once = false): void {
        $this->listeners[$event][$priority][] = [$listener, $once];
        krsort($this->listeners[$event]); // 高優先級先執行
    }

    public function once(string $event, callable $listener, int $priority = 0): void {
        $this->on($event, $listener, $priority, true);
    }

    public function off(string $event, callable $listener = null): void {
        if ($listener === null) {
            unset($this->listeners[$event]);
        } else {
            foreach ($this->listeners[$event] as $p => $arr) {
                $this->listeners[$event][$p] = array_filter($arr, fn($a) => $a[0] !== $listener);
            }
        }
    }

    public function emit(string $event, mixed $payload = null): void {
        if (!isset($this->listeners[$event])) return;
        foreach ($this->listeners[$event] as $priority => $arr) {
            foreach ($arr as $i => [$listener, $once]) {
                $listener($payload);
                if ($once) unset($this->listeners[$event][$priority][$i]);
            }
        }
    }
}

// 使用示例
$dispatcher = new EventDispatcher();

$dispatcher->on('order.created', fn($order) => Logger::info("Order {$order['id']} created"));
$dispatcher->on('order.created', fn($order) => Email::send($order['email'], '確認訂單'), 10); // 高優先級
$dispatcher->once('order.paid', fn($order) => SMS::send($order['phone'], '付款成功'));

$dispatcher->emit('order.created', ['id' => 1001, 'email' => 'a@b.com']);
$dispatcher->emit('order.paid', ['id' => 1001, 'phone' => '13800000000']);
?>

PHP 內置支持:SplSubject/SplObserver

PHP SPL 提供標準接口,框架(如 Laravel、Symfony)事件系統均基於此擴展:

class Order implements SplSubject {
    private array $observers = [];
    public function attach(SplObserver $o) { $this->observers[] = $o; }
    public function detach(SplObserver $o) { /* ... */ }
    public function notify() { foreach ($this->observers as $o) $o->update($this); }
}
class EmailNotifier implements SplObserver {
    public function update(SplSubject $subject) { /* 發送郵件 */ }
}

模式優勢與適用場景

優勢說明
低耦合主題無需知道觀察者具體類,只依賴抽象接口
廣播通信一條消息自動分發給多個訂閱者
動態訂閱運行時自由增減觀察者,支持插件化架構
單一職責業務邏輯與副作用(日志、通知、緩存失效)分離

典型場景

  • 模型層事件:UserRegistered、OrderPaid、StockChanged
  • 緩存失效:數據更新後自動通知緩存服務清理相關鍵
  • 插件/掛鉤系統:核心流程暴露事件點,插件訂閱擴展功能
  • 實時通知:WebSocket 服務器訂閱業務事件推送前端
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved