Table of Contents
Introduction
Observer Pattern 是一種行為設計模式,主要解決的是「如何在不緊密耦合主題和觀察者的情況下,使多個觀察者對象都能夠即時更新其狀態。」通過這種方式,任何關於主題的變更會立即通知所有註冊的觀察者。
什麼是 Observer Pattern
Observer Pattern(觀察者模式)其實就像是一個郵件訂閱系統。想像你訂閱了一家咖啡店的電子報,每當他們推出新品或有特價活動,你會收到通知。
在這個例子中,咖啡店就是「主題」,而你和其他訂閱了電子報的客戶就是「觀察者」。主題和觀察者之間不需要相互了解細節,只要主題有更新,所有的觀察者都會得到通知。
這樣的好處是,如果未來有更多人想訂閱咖啡店的電子報,或者有人想取消訂閱,咖啡店不需要做大的改動,只需要在觀察者列表中新增或移除人就好。
簡單來說,Observer Pattern 就是一個讓你能簡單地通知一群人(觀察者)有關某件事(主題)變動的方法。
什麼狀況適合使用 Observer Pattern
- 當你希望維護多個對象間的一致性,而不希望它們之間緊密耦合。
- 當一個對象的改變需要改變其他多個對象,但你又不確定會有多少對象需要改變。
範例教學
假設我們有一個咖啡店模擬程序,需要在新品種的咖啡推出時通知所有註冊的客戶。
原始範例 (未使用 Observer)
<?php
// 舊方法,需要手動更新每一個客戶
class CoffeeShop {
public $customers = [];
public function newCoffeeAvailable($coffeeType) {
foreach ($this->customers as $customer) {
// 假設每個客戶都有一個 notify 方法
$customer->notify("New coffee available: $coffeeType");
}
}
}
?>
缺點:這個方法會讓 CoffeeShop
和 Customer
緊密耦合,並且不易於擴展。
改善範例 (使用 Observer)
首先建立一個使用 Observer Pattern 的咖啡訂單追蹤系統。在這個範例中,CoffeeOrder
是主題,而 Customer
是觀察者。每當有新的咖啡訂單狀態更新,所有訂閱(觀察)該訂單的客戶會收到通知。
以下是具體的 PHP 程式碼:
<?php
// 主題介面
interface Subject {
public function attach($observer);
public function detach($observer);
public function notify();
}
// 觀察者介面
interface Observer {
public function update($message);
}
// CoffeeOrder 類別實現 Subject
class CoffeeOrder implements Subject {
private $observers = [];
private $status;
public function attach($observer) {
$this->observers[] = $observer; // 新增觀察者
}
public function detach($observer) {
$index = array_search($observer, $this->observers); // 找到觀察者的索引
unset($this->observers[$index]); // 移除觀察者
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this->status); // 更新所有觀察者
}
}
public function setStatus($status) {
$this->status = $status; // 更新狀態
$this->notify(); // 通知所有觀察者
}
}
// Customer 類別實現 Observer
class Customer implements Observer {
public function update($message) {
echo "Customer received message: " . $message . PHP_EOL; // 接收更新
}
}
// 使用範例
$order = new CoffeeOrder();
$customer1 = new Customer();
$customer2 = new Customer();
$order->attach($customer1); // 客戶 1 訂閱訂單
$order->attach($customer2); // 客戶 2 訂閱訂單
$order->setStatus('Your coffee is ready!'); // 更新訂單狀態,客戶會收到通知
?>
這個例子的優點是,當 CoffeeOrder
的狀態更新時,所有訂閱這個訂單的 Customer
會自動得到通知,而我們不需要改變 CoffeeOrder
類別的程式碼。這樣增加了程式的靈活性和可擴展性。
更多延伸範例
觀察者模式(Observer Pattern)非常適合在多個對象之間建立一對多的依賴關係。除了上面的咖啡訂單追蹤範例外,這裡還有其他一些例子來展示類似用途。
假設咖啡店有不同種類的促銷活動,如「買一送一」或「會員日」等。我們可以使用觀察者模式來通知訂閱了不同促銷活動的客戶。
// 主題介面,定義觀察者模式需要的基本方法
interface Subject {
public function attach($observer);
public function detach($observer);
public function notify();
}
// 觀察者介面,定義觀察者需要的更新方法
interface Observer {
public function update($message);
}
// Promotion 是 Subject 的具體實現,管理和通知觀察者
class Promotion implements Subject {
private $observers = []; // 存放觀察者的數組
private $promotionType; // 促銷活動類型
// 添加一個新的觀察者
public function attach($observer) {
$this->observers[] = $observer;
}
// 刪除一個觀察者
public function detach($observer) {
$index = array_search($observer, $this->observers);
unset($this->observers[$index]);
}
// 通知所有觀察者
public function notify() {
foreach ($this->observers as $observer) {
$observer->update("New promotion: " . $this->promotionType);
}
}
// 設定新的促銷活動並通知所有觀察者
public function setPromotion($promotionType) {
$this->promotionType = $promotionType;
$this->notify();
}
}
// Customer 是 Observer 的具體實現,它會被通知到 Promotion 的變更
class Customer implements Observer {
private $name;
public function __construct($name) {
$this->name = $name;
}
// 更新,這裡只是簡單地輸出消息
public function update($message) {
echo $this->name . " received update: " . $message . PHP_EOL;
}
}
// 創建一個 Promotion 實例和兩個 Customer 實例
$promotion = new Promotion();
$alice = new Customer("Alice");
$bob = new Customer("Bob");
// Alice 和 Bob 訂閱促銷活動
$promotion->attach($alice);
$promotion->attach($bob);
// 設定一個新的促銷活動,Alice 和 Bob 會被通知
$promotion->setPromotion("Buy One Get One Free");
程式運作說明
- 我們定義了一個
Subject
介面和一個Observer
介面,以設定觀察者模式需要的基本方法。 Promotion
是Subject
的具體實現。它管理一個觀察者列表($observers
)和當前的促銷活動類型($promotionType
)。attach
方法用於添加新的觀察者,而detach
方法用於刪除觀察者。- 當促銷活動變更(
setPromotion
方法被調用)時,notify
方法會遍歷觀察者列表並調用他們的update
方法,將最新的促銷信息發送給他們。 - 我們創建了兩個
Customer
類的實例(Alice
和Bob
)並讓他們訂閱促銷活動。 - 當我們設定一個新的促銷活動時,所有訂閱的
Customer
(這裡是Alice
和Bob
)會收到更新通知。
這種設計讓我們可以簡單地添加或刪除觀察者,並且當促銷活動發生變更時,所有的觀察者都會自動獲得通知。這提供了一個高度解耦和可擴展的設計。
其他類似的 Design Pattern
類似的模式有「Publish-Subscribe Pattern」和「Event Aggregator」。
- Observer 通常是一對多。
- 而 Publish-Subscribe 可以是多對多。
- Event Aggregator 提供一個更集中的事件管理機制。