PHP 5.1+ 內建 SplSubject 與 SplObserver 介面,標準化觀察者模式實作。本文展示使用 SPL 介面建立 DemoSubject (被觀察者) 與 DemoObserver (觀察者) 的完整代碼,涵蓋 attach()/detach()/notify() 管理觀察者清單、狀態變更觸發通知、update() 接收推送資料,並解析 SPL 標準與自行定義介面的差異。
interface SplSubject {
public function attach(SplObserver $observer);
public function detach(SplObserver $observer);
public function notify();
}
interface SplObserver {
public function update(SplSubject $subject);
}
<?php
class DemoSubject implements SplSubject {
private $observers = array();
private $value;
public function __construct() {
$this->observers = array();
}
// 註冊觀察者
public function attach(SplObserver $observer) {
$this->observers[] = $observer;
}
// 移除觀察者
public function detach(SplObserver $observer) {
$idx = array_search($observer, $this->observers, true);
if ($idx !== false) {
unset($this->observers[$idx]);
}
}
// 通知所有觀察者
public function notify() {
foreach ($this->observers as $observer) {
// 推模式:傳遞主題自身,觀察者自行取值
$observer->update($this);
}
}
// 設定值並觸發通知
public function setValue($value) {
$this->value = $value;
$this->notify();
}
public function getValue() {
return $this->value;
}
}
class DemoObserver implements SplObserver {
public function update(SplSubject $subject) {
// 觀察者從主題拉取所需資料 (拉模式)
echo 'The new value is ' . $subject->getValue() . "\n";
}
}
// 客戶端測試
$subject = new DemoSubject();
$observer = new DemoObserver();
$subject->attach($observer);
$subject->setValue(5); // 輸出: The new value is 5
$subject->setValue(10); // 輸出: The new value is 10
$subject->detach($observer);
$subject->setValue(20); // 無輸出
?>
| 特性 | SPL 標準介面 | 自定義介面 (Head First 範例) |
|---|---|---|
| 介面名稱 | SplSubject / SplObserver | Subject / Observer / DisplayElement |
| 通知方法 | notify() 無參數 | notifyObservers() 無參數 |
| 更新方法 | update(SplSubject $subject) | update($temp, $humidity, $pressure) |
| 資料傳遞模式 | 拉模式:觀察者從 Subject 取值 | 推模式:Subject 推送資料給觀察者 |
| 觀察者管理 | attach/detach (單數) | registerObserver/removeObserver |
| 型別提示 | 強制 SplObserver/SplSubject | 自定義介面 |
| 相容性 | PHP 內建,第三方庫可直接互操作 | 專案內部使用 |
notify() 不帶參數,update(SplSubject $subject) 由觀察者呼叫 $subject->getValue() 等方法主動取值。優點:觀察者按需取值,Subject 不需知道觀察者需要什麼資料。缺點:觀察者耦合 Subject 具體方法。notifyObservers() 內部遍歷呼叫 $ob->update($temp, $humidity, $pressure) 直接推送資料。優點:觀察者完全被動,不依賴 Subject 介面。缺點:Subject 需知道觀察者需要什麼資料,或推送所有資料。class AdvancedSubject implements SplSubject {
private $observers = array();
private $data = array();
public function attach(SplObserver $observer) { ... }
public function detach(SplObserver $observer) { ... }
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function setData($key, $value) {
$this->data[$key] = $value;
$this->notify();
}
public function getData($key) {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
}
class LoggerObserver implements SplObserver {
public function update(SplSubject $subject) {
// 記錄所有變更
error_log("Data changed: " . json_encode($subject->getData()));
}
}
class AlertObserver implements SplObserver {
public function update(SplSubject $subject) {
// 只關心特定閾值
if ($subject->getData('temperature') > 100) {
echo "ALERT: Temperature critical!\n";
}
}
}
PHP SPL 提供標準化觀察者模式介面 SplSubject/SplObserver,採用拉模式 (notify 無參、update 取主題)。核心流程:Subject 維護觀察者陣列 → attach/detach 管理 → 狀態變更呼叫 notify() → 遍歷呼叫 update($this) → 觀察者從 Subject 取值。使用 SPL 介面可確保與第三方庫互操作性,是現代 PHP 專案實作觀察者模式的首選標準。