PHP之觀察者模式
- 觀察者模式在PHP裡面是有已經定義好的兩個介面檔案,我們只需要繼承它並實現它,那麼我們的觀察者模式就已經算是實現了。
首先觀察者模式,顧名思義,就是需要有一個觀察者和被觀察者,被觀察的物件發生了變化,可以及時通知所有觀察這個物件變化的物件
在PHP種預定義的觀察者:
\SplObserver
觀察者定義了一個介面:
interface SplObserver
{
public function update(SplSubject $subject);
}
- 在PHP種預定義的被觀察者:
\SplSubject
,這個被觀察者定義了三個介面方法
interface SplSubject
{
public function attach(SplObserver $observer);
public function detach(SplObserver $observer);
public function notfiy();
}
很明顯,在觀察者方法裡,如果有變化了就呼叫
update()
方法; 在被觀察裡,第一個方法attach()
,新增一個被觀察者到一個自己維護的資料結構中,
這個結構可以是PHP的陣列array
, 可以是一個集合,可以是一個has
表,具體的怎麼實現,都可以自己操作。detach()
這個方法就是從之維護的一個結
構中,刪掉這個被觀察者。最後一個核心的方法就是notfiy
,它是從之前的資料儲存結構中,遍歷所有的觀察者,呼叫觀察者裡的方法,通知它需要做事情了。之前第四步說的儲存註冊觀察者的資料結構,可以是一個陣列,可以是一個集合,同時還可以是PHP自己實現的一個
\SplObjectStorage
的一個管理類
SplObjectStorage 在裡面它實現了很多方法,包括但不限於attach(), detach()
是一個很強大的類,可以幫我們做這些事情。如下:
class SplObjectStorage implements \Countable, \Iterator, \Serializable, \ArrayAccess {
public function attach ($object, $data = null) {}
public function detach ($object) {}
public function contains ($object) {}
public function addAll ($storage) {}
public function removeAll ($storage) {}
public function removeAllExcept ($storage) {}
public function getInfo () {}
public function setInfo ($data) {}
/**
* @inheritDoc
*/
public function current()
{
// TODO: Implement current() method.
}
/**
* @inheritDoc
*/
public function next()
{
// TODO: Implement next() method.
}
/**
* @inheritDoc
*/
public function key()
{
// TODO: Implement key() method.
}
/**
* @inheritDoc
*/
public function valid()
{
// TODO: Implement valid() method.
}
/**
* @inheritDoc
*/
public function rewind()
{
// TODO: Implement rewind() method.
}
/**
* @inheritDoc
*/
public function offsetExists($offset)
{
// TODO: Implement offsetExists() method.
}
/**
* @inheritDoc
*/
public function offsetGet($offset)
{
// TODO: Implement offsetGet() method.
}
/**
* @inheritDoc
*/
public function offsetSet($offset, $value)
{
// TODO: Implement offsetSet() method.
}
/**
* @inheritDoc
*/
public function offsetUnset($offset)
{
// TODO: Implement offsetUnset() method.
}
/**
* @inheritDoc
*/
public function serialize()
{
// TODO: Implement serialize() method.
}
/**
* @inheritDoc
*/
public function unserialize($serialized)
{
// TODO: Implement unserialize() method.
}
/**
* @inheritDoc
*/
public function count()
{
// TODO: Implement count() method.
}
}
使用一個觀者者模式解決我們業務上的一個邏輯,如下:純屬虛構。
假如現在有一個天氣預報的平臺,它是一個權威的檢測和釋出天氣的平臺,它得到最新的天氣後,會把最新的天氣情況轉送給給大媒體平臺,這樣我們不管在那個
媒體平臺都可以看到最新的天氣情況,假設現在有新浪天氣,百度天氣,騰訊天氣,墨跡天氣。。。假設是有這四家來得到天氣的變化情況,然後免費更新。那麼
現在這個權威的天氣檢測平臺就是一個被觀察者,而那四家媒體平臺就是觀察者。現在建立一個被觀察者媒體平臺
class Weather implements \SplSubject
{
/**
* 管理所有觀察者
* @var SplObjectStorage
*/
private $observers;
// 最新的天氣情況
private $email;
public function __construct()
{
$this->observers = new \SplObjectStorage();
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
/**
* @inheritDoc 註冊觀察者
*/
public function attach(SplObserver $observer)
{
// TODO: Implement attach() method.
$this->observers->attach($observer);
}
/**
* @inheritDoc 登出觀察者
*/
public function detach(SplObserver $observer)
{
// TODO: Implement detach() method.
$this->observers->detach($observer);
}
// 天氣改變後,通知所有的觀察者
public function changeWeather(string $email)
{
$this->setEmail($email)->notify();
}
/**
* @inheritDoc 通知所有的觀察者
*/
public function notify()
{
// TODO: Implement notify() method.
foreach ($this->observers as $observer) {
$observer->update($this);//呼叫觀察的update方法
}
}
}
- 建立四個觀察者
class Baidu implements \SplObserver
{
/**
* @inheritDoc
*/
public function update(SplSubject $subject)
{
// TODO: Implement update() method.
echo '給百度發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
}
}
class Tencent implements \SplObserver
{
/**
* @inheritDoc
*/
public function update(SplSubject $subject)
{
// TODO: Implement update() method.
echo '給騰訊發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
}
}
class MoJi implements \SplObserver
{
/**
* @inheritDoc
*/
public function update(SplSubject $subject)
{
// TODO: Implement update() method.
echo '給墨跡發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
}
}
class Sina implements \SplObserver
{
/**
* @inheritDoc
*/
public function update(SplSubject $subject)
{
// TODO: Implement update() method.
echo '給新浪發郵件了, 天氣變化:' . $subject->getEmail() . PHP_EOL;
}
}
- 客戶端測試呼叫
class Client
{
public function __construct(string $email)
{
$weather = new Weather();
$baidu = new Baidu();
$sina = new Sina();
$moji = new MoJi();
$tencent = new Tencent();
$weather->attach($baidu);
$weather->attach($sina);
$weather->attach($moji);
$weather->attach($tencent);
$weather->changeWeather($email);
}
}
new Client('今天的天氣溫度是 36.6攝氏度,注意高溫防曬');
new Client('今天的天氣溫度是 40.0攝氏度,注意高溫防曬');
new Client('今天的天氣溫度是 22.0攝氏度,溫度剛剛好');
- 當我們天氣變化的時候,就直接呼叫
changeWeather()
方法,這樣四家媒體平臺就都得到最新的天氣情況了
本作品採用《CC 協議》,轉載必須註明作者和本文連結
LIYi ---- github地址