什麼是觀察者模式?
- 從程式導向的角度來看,首先是觀察者向主題註冊,註冊完之後,主題再通知觀察者做出相應的操作,整個事情就完了
- 從物件導向的角度來看,主題提供註冊和通知的介面,觀察者提供自身操作的介面。(這些觀察者擁有一個同一個介面。)觀察者利用主題的介面向主題註冊,而主題利用觀察者介面通知觀察者。耦合度相當之低
流程圖如下:
為什麼要用觀察者模式?
- 觀察者模式更多體現了兩個獨立的類利用介面完成一件本應該很複雜的事情。不利用主題類的話,我們還需要不斷迴圈建立例項,執行操作。而現在只需要建立例項就好,執行操作的事兒只需要呼叫一次通知的方法就好啦
應用場景
- 當一個物件的改變需要同時改變其他物件的時候,而且它不知道具體有多少物件有待改變時,應該考慮使用觀察者模式。
程式碼一
/**
* @purpose: 觀察者介面, 定義觀察者具體需要執行的方法,當然方法名和方法個數可以自定義
* Interface Observer
*/
interface Observer
{
/**
* @purpose: 廣播通知後,所有已註冊的觀察者都需要執行該方法。
* @return mixed
*/
public function eat();
}
/**
* @purpse: 定義貓貓類,繼承觀察者介面,實現具體細節
* Class Cat
*/
class Cat implements Observer
{
public function eat()
{
echo 'Cat eat fish';
}
}
/**
* @purpse: 定義狗狗類,繼承觀察者介面,實現具體細節
* Class Dog
*/
class Dog implements Observer
{
public function eat()
{
echo 'Dog eat bones';
}
}
/**
* @purpose: 主題介面, 定義新增觀察者和廣播通知的方法
* Interface Notify
*/
interface Subject
{
/**
* @purpose: 新增觀察者
* @param string $key 給所新增的觀察者的一個唯一 key,方便從註冊樹中移除觀察者
* @param Observer $observer 觀察者物件
* @return mixed
*/
public function addObserver($key, Observer $observer);
/**
* @purpose: 從註冊樹中移除觀察者
* @param string $key 給所新增的觀察者的一個唯一 key,方便從註冊樹中移除觀察者
* @return mixed
*/
public function removeObserver($key);
/**
* @purpose: 廣播通知以註冊的觀察者
* @return mixed
*/
public function notify();
}
/**
* @purpose: 實現主體介面,主要就是新增觀察者和廣播通知觀察者
* Class Action
*/
class Action implements Subject
{
/**
* @var array 儲存所有已註冊的觀察者
*/
public $_observer = [];
/**
* @purpose: 新增觀察者
* @param string $key 給所新增的觀察者的一個唯一 key,方便從註冊樹中移除觀察者
* @param Observer $observer 觀察者物件
* @return mixed
*/
public function addObserver($key, Observer $observer)
{
$this->_observer[$key] = $observer;
}
/**
* @purpose: 從註冊樹中移除觀察者
* @param string $key 給所新增的觀察者的一個唯一 key,方便從註冊樹中移除觀察者
* @return mixed
*/
public function removeObserver($key)
{
unset($this->_observer[$key]);
}
/**
* @purpose: 廣播通知以註冊的觀察者,對註冊樹進行遍歷,讓每個物件實現其介面提供的操作
* @return mixed
*/
public function notify()
{
foreach ($this->_observer as $observer) {
$observer->eat();
}
}
}
程式碼二
<?php
/*
觀察者介面
*/
interface InterfaceObserver
{
function onListen($sender, $args);
function getObserverName();
}
// 可被觀察者介面
interface InterfaceObservable
{
function addObserver($observer);
function removeObserver($observer_name);
}
// 觀察者抽象類
abstract class Observer implements InterfaceObserver
{
protected $observer_name;
function getObserverName()
{
return $this->observer_name;
}
function onListen($sender, $args)
{
}
}
// 可被觀察類
abstract class Observable implements InterfaceObservable
{
protected $observers = array();
public function addObserver($observer)
{
if ($observer instanceof InterfaceObserver)
{
$this->observers[] = $observer;
}
}
public function removeObserver($observer_name)
{
foreach ($this->observersas $index => $observer)
{
if ($observer->getObserverName() === $observer_name)
{
array_splice($this->observers, $index, 1);
return;
}
}
}
}
// 模擬一個可以被觀察的類
class A extends Observable
{
public function addListener($listener)
{
foreach ($this->observers as $observer)
{
$observer->onListen($this, $listener);
}
}
}
// 模擬一個觀察者類
class B extends Observer
{
protected $observer_name = 'B';
public function onListen($sender, $args)
{
var_dump($sender);
echo "<br>";
var_dump($args);
echo "<br>";
}
}
// 模擬另外一個觀察者類
class C extends Observer
{
protected $observer_name = 'C';
public function onListen($sender, $args)
{
var_dump($sender);
echo "<br>";
var_dump($args);
echo "<br>";
}
}
$a = new A();
// 注入觀察者
$a->addObserver(new B());
$a->addObserver(new C());
// 可以看到觀察到的資訊
$a->addListener('D');
// 移除觀察者
$a->removeObserver('B');
// 列印的資訊:
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
// string(1) "D"
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
// string(1) "D"
參考
www.cnblogs.com/chrdai/p/11184221....
本作品採用《CC 協議》,轉載必須註明作者和本文連結