觀察者模式(又稱爲發佈-訂閱(Publish/Subscribe)模式,屬於行爲型模式的一種,它是將行爲獨立模塊化,下降了行爲和主體的耦合性。它定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態變化時,會通知全部的觀察者對象,使他們可以自動更新本身。php
Subject:抽象主題(抽象被觀察者),抽象主題角色把全部觀察者對象保存在一個集合裏,每一個主題均可以有任意數量的觀察者,抽象主題提供一個接口,能夠增長和刪除觀察者對象。模塊化
ConcreteSubject:具體主題(具體被觀察者),該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給全部註冊過的觀察者發送通知。this
Observer:抽象觀察者,是觀察者者的抽象類,它定義了一個更新接口,使得在獲得主題更改通知時更新本身。code
ConcrereObserver:具體觀察者,是實現抽象觀察者定義的更新接口,以便在獲得主題更改通知時更新自身的狀態。server
PHP 內置了對象
SplSubject 抽象主題 Interface接口
SplObserver 抽象觀察者 Interface事件
接口約束ip
// 主題 被觀察者 interface SplSubject { public function attach(SplObserver $observer); //註冊觀察者到當前主題 public function detach(SplObserver $observer); //從當前主題刪除觀察者 public function notify(); //主題狀態更新時通知全部的觀察者作相應的處理 } // 觀察者 interface SplObserver { public function update(SplSubject $subject); //註冊觀察者到當前主題 }
經過項目中的實際應用能更容易的去理解觀察者模式開發
下面咱們以用戶爲主題,郵件模塊和短信模塊爲觀察者
當用戶註冊成功時,郵件觀察者或短信觀察者則收到相應的通知,發送郵件和短信給用戶
User 主題
<?php /** * 主題類(被觀察者至關於一個主題,觀察者訂閱這個主題) * 當咱們註冊用戶成功的時候想發送 email 和 sms 通知用戶註冊成功 * 則 能夠將 SendEmail 和 SendSms 做爲觀察者 * 註冊到 User 的觀察者中 * 當 User register 成功時 notify 給 observers * 各 observe 經過約定的 update 接口進行相應的處理 發郵件或發短信 */ class User implements SplSubject { public $name; public $email; public $mobile; /** * 當前主題下的觀察者集合 * @var array */ private $observers = []; /** * 模擬註冊 * @param [type] $name [description] * @param [type] $email [description] * @param [type] $mobile [description] * @return [type] [description] */ public function register($name, $email, $mobile) { $this->name = $name; $this->email = $email; $this->mobile = $mobile; //business handle and register success $reg_result = true; if ($reg_result) { $this->notify(); // 註冊成功 全部的觀察者將會收到此主題的通知 return true; } return false; } /** * 當前主題註冊新的觀察者 * @param SplObserver $observer [description] * @return [type] [description] */ public function attach(SplObserver $observer) { return array_push($this->observers, $observer); } /** * 當前主題刪除已註冊的觀察者 * @param SplObserver $observer [description] * @return [type] [description] */ public function detach(SplObserver $observer) { $key = array_search($observer, $this->observers, true); if (false !== $key) { unset($this->observers[$key]); return true; } return false; } /** * 狀態更新 通知全部的觀察者 * @return [type] [description] */ public function notify() { if (! empty($this->observers)) { foreach ($this->observers as $key => $observer) { $observer->update($this); } } return true; } }
Email/Sms 觀察者
/** * 觀察者經過 update 來接受主題的更新通知 */ class EmailObserver implements SplObserver { /** * 觀察者接收主題通知的接口 * @param SplSubject $user [description] * @return [type] [description] */ public function update(SplSubject $user) { echo "send email to " . $user->email . PHP_EOL; } } class SmsObserver implements SplObserver { public function update(SplSubject $user) { echo "send sms to " . $user->mobile . PHP_EOL; } }
業務
// User 主題 $user = new User(); // 爲 user 註冊 Email 觀察者 (Email 觀察者訂閱 User 主題) $emailObserver = new EmailObserver(); $user->attach($emailObserver); // 爲 user 註冊 Sms 觀察者 (Sms 觀察者訂閱 User 主題) $smsObserver = new SmsObserver(); $user->attach($smsObserver); // 從 user 上刪除 Sms 觀察者 (Sms 觀察者取消訂閱 User 主題) //$user->detach($smsObserver); // register 中會根據註冊結果通知觀察者 觀察者作相應的處理 $user->register("big cat", "32448732@qq.com", "1888888888");
結果
send email to 32448732@qq.com send sms to 1888888888 [Finished in 0.1s]
其實觀察者模式相似於事件註冊和鉤子回調,平常開發中咱們可能重構分離出一部分類的行爲到外部,封裝成獨立的功能模塊,在註冊到類中,能夠使用事件註冊,也能夠使用觀察者模式