什麼是觀察者模式
觀察者模式用於實現對物件進行觀察:一旦主體物件狀態發生改變,與之關聯的觀察者物件會收到通知,並進行相應操作。
舉個例子說明:
假設一個這樣的情景,當公司有一個新員工入職了,入職的當天,HR需要為他辦理入職手續,網管需要給他配好電腦和辦公用品,部門主管需要帶他熟悉部門。傳統的程式設計方式,就是在員工入職這個事件發生的程式碼之後直接加入處理邏輯,當後續我們需要增加處理邏輯時(比如員工入職後增加培訓),程式碼會變得難以維護。這種方式是耦合的,侵入式的,增加新的邏輯需要改變事件主題的程式碼。運用觀察者模式,將員工的入職作為事件,其他的處理邏輯都做為觀察者的操作,那麼,當以後需要再增加更多的邏輯時,新增邏輯程式碼就會很方便。具體程式碼實現如下。
程式碼實現
首先定義一個觀察者介面,所有的觀察者都實現這個介面(為什麼要定義成介面呢?因為每一個觀察者的具體行為需要具體去實現,用介面定義一個統一的方法,具體的實現交給觀察者去實現)
interface observer
{
public function update();
}
再定義一個事件生成器的抽象類,用來使繼承它的事件都具有通知觀察者的能力。
abstract class EventGenerator
{
private $observers = [];
//定義一個新增觀察者的方法
public function addOberver(Observer $observer)
{
$this->observers[] = $observer;
}
//定義一個通知觀察者的方法
public function notify()
{
foreach($this->observers as $observer)
{
$observer->update();
}
}
}
然後再來定義事件類
class event extends EventGenerator
{
//定義一個觸發觀察者的方法
public funtion trigger()
{
echo "Event <br/>";
//開始通知觀察者
$this->notify();
}
}
開始使用
$event = new event();
$event->trigger();
這個時候,當我們要在事件發生的時候增加別的操作,只需要新增觀察者就可以了
新增一個觀察者
class Observer1 implements Observer
{
public function update()
{
echo "操作1<br/>";
}
}
然後使用的時候就是
$event = new event();
// 增加觀察者
$event->addObserver(new Observer1);
$event->trigger();
如果需要在事件發生後再增加操作,只需再新增相應的觀察者即可。
應用觀察者模式的好處
觀察者模式解除了主體和具體觀察者的耦合,讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。降低物件之間的耦合度以達到解耦的目的,符合"開閉原則"的要求。
利用 SPL 實現觀察者模式
PHP 通過內建的 SPL 擴充套件提供了對觀察者模式的原生支援,其中的觀察者由 3 個元素組成 : SplObserver 介面、 SplSubject 介面和 SplObjectStorage 工具類。下面是利用 SPL 實現觀察者模式的程式碼。SPL 的地址見這裡。
class MyObserver1 implements SplObserver {
public function update(SplSubject $subject) {
echo __CLASS__ . ' - ' . $subject->getName();
}
}
class MyObserver2 implements SplObserver {
public function update(SplSubject $subject) {
echo __CLASS__ . ' - ' . $subject->getName();
}
}
class MySubject implements SplSubject {
private $observers;
private $name;
public function __construct($name) {
$this->observers = new SplObjectStorage();
$this->name = $name;
}
public function attach(SplObserver $observer) {
$this->observers->attach($observer);
}
public function detach(SplObserver $observer) {
$this->observers->detach($observer);
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function getName() {
return $this->name;
}
}
$observer1 = new MyObserver1();
$observer2 = new MyObserver2();
$subject = new MySubject("test");
$subject->attach($observer1);
$subject->attach($observer2);
$subject->notify();
/*
輸出:
MyObserver1 - test
MyObserver2 - test
*/
$subject->detach($observer2);
$subject->notify();
/*
輸出:
MyObserver1 - test
*/