<?php
interface SplSubject{
public function attach(SplObserver $observer);//注冊觀察者
public function detach(SplObserver $observer);//釋放觀察者
public function notify();//通知所有注冊的觀察者
}
interface SplObserver{
public function update(SplSubject $subject);//觀察者進行更新狀態(tài)
}
具體目標(被觀察者)
class News implements SplSubject
{
public $pid = 0;
public $observers = [];
public function __construct($pid)
{
$this->pid = $pid;
/**
* 實現(xiàn)php 自動加載
*/
spl_autoload_register([$this, 'loadRegister']);
/**
* 這里是加載 Plug 目錄下 所有插件
*/
$this->autoRegister();
}
/**
* 實現(xiàn)具體自動加載步驟
*
* 這里沒有 遵循 psr 規(guī)范 強烈不建議
*
* 我這么寫只是方便
*
* @param $class_name
*/
public function loadRegister($class_name)
{
$class_name = str_replace("\\", "/", $class_name);
include dirname(__DIR__) . '/' . $class_name . '.php';
}
/**
* 加載插件方法
*
* 這里是為了方便 一次性加載 Plug 下所有插件
*
* 并注冊觀察者
*/
public function autoRegister()
{
$path = __DIR__ . "/plug/";
$directory = dir($path);
while ($file = $directory->read()) {
if ($file == '.' || $file == "..") {
continue;
}
$class_name = basename($file, '.php');//NewsClick
/**
* 這里是因為有命名空間 所以要這么寫 完全限定名稱
*
* 關于更多命名空間直接參考手冊 很詳細
*/
$str = '\\News\\Plug\\' . $class_name;
// 在實例化的時候 直接把當前類傳給要實例的目標 如果目標需要可以自行注冊
$object = new $str($this);
//判斷是否是 SplObserver 的子類 如果是直接注冊為觀察者
if (is_subclass_of($object, 'SplObserver', true)) {
$this->attach($object);
}
}
}
/**
* 添加一個觀察者
*
* @param \SplObserver $observer
*/
public function attach(SplObserver $observer)
{
$this->observers[strval($observer)] = $observer;
}
/**
* 刪除一個觀察者
*
* @param \SplObserver $observer
*/
public function detach(SplObserver $observer)
{
if (array_search($observer, $this->observers, true)) {
unset($this->observers[strval($observer)]);
}
}
/**
* 當狀態(tài)改變時 通知所有觀察者
*/
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
/**
* 相當于改變狀態(tài)的方法 改變狀態(tài)后調用 notify
*/
public function read()
{
echo "閱讀了 product id " . $this->pid . " 的文章";
$this->notify();
}
}
具體觀察者
// 這類繼承 SplObserver 目標(被觀察者)會直接將其注冊為觀察者
class NewsClick implements SplObserver
{
/**
* 接收到通知后你做什么由你來決定
*
* @param \SplSubject $subject
*/
public function update(SplSubject $subject)
{
echo "<hr>";
echo 'product ID' . $subject->pid . '點擊量加1';
}
/**
* 這個模式方法讓我用了做 key 了 你隨意
*
* @return string
*/
public function __toString()
{
return "news_click";
}
}
// 這個類沒有繼承 SplObserver 所以需要自己來添加觀察者
class NewsLike
{
public function __construct(SplSubject $news)
{
$news->attach(new class implements SplObserver
{
/**
* 接收到通知后你做什么由你來決定
*
* @param \SplSubject $subject
*/
public function update(SplSubject $subject)
{
echo "<hr>";
echo 'product ID' . $subject->pid . '收藏量加1';
}
/**
* 這個模式方法讓我用了做 key 了 你隨意
*
* @return string
*/
public function __toString()
{
return "news_like";
}
});
}
}
調用
//因為使用了自動加載 代碼變少了
use news\News;
$news = new News(100);
$news->read();
結果
閱讀了 product id 100 的文章<hr>product ID100收藏量加1<hr>product ID100點擊量加1<hr>product ID100日志加1