介紹
定義:觀察者模式(Observer)有時又被稱為發布/訂閱模式,當一個對象狀態發生改變時,依賴它的對象全部會收到通知,并自動更新。
場景:一個事件發生后,要執行一連串更新操作。傳統的編程方式,就是在事件的代碼之后直接加入處理邏輯。當更新的邏輯增加多之后,代碼會變得難以維護。這種方式是耦合的,入侵式的,增加新的邏輯需要修改事件主體的代碼。
特點:觀察者模式實現了低耦合,非入侵式的通知與更新機制。
UML類圖:
抽象觀察者角色(IObserver):定義接到通知后所做的操作(Update)接口規則。
具體觀察者角色(Log_Obsecer / Mail_Observer):實現具體操作方法。
抽象通知者角色(Exception_Notify):定義了通知的接口規則。
具體通知者角色(MyException):實現抽象通知者的接口,接到狀態改變立即向觀察者下發通知。
使用案例
本人在這里用觀察者模式寫一個異常捕獲的通知功能,核心代碼如下:
抽象觀察者角色(IObserver)
//?定義觀察者接口規范——觀察者模式
interface IObserver{
? ? public function update(Exception_Notify $e);
}
具體觀察者角色1(Log_Obsecer)
// 具體觀察者(日志通知)
class Log_Observer implements IObserver{
? ? protected $_filename = './error.log';
? ? public function __construct($filename=null) {
? ? ? ? if($filename !==null && is_string($filename)){
? ? ? ? ? ? $this->_filename = $filename;
? ? ? ? }
? ? }
? ? public function update(Exception_Notify $e) {
? ? ? ? $message = 'Time: '.date('Y-m-d H:i:s').PHP_EOL;
? ? ? ? $message .= 'Info: '.$e->getMessage().PHP_EOL;
? ? ? ? $message .= 'File: '.$e->getFile().PHP_EOL;
? ? ? ? $message .= 'Line: '.$e->getLine().PHP_EOL;
? ? ? ? error_log($message,3,$this->_filename);
? ? }
}
具體觀察者角色2(Mail_Observer)
// 具體觀察者(郵箱通知)
class Mail_Observer implements IObserver {
? ? protected $_email = '273788888@qq.com';
? ? public function __construct($email=null) {
? ? ? ? if($email !== null && filter_var($email,FILTER_VALIDATE_EMAIL)){
? ? ? ? ? ? $this->_email = $email;
? ? ? ? }
? ? }
? ? public function update(Exception_Notify $e) {
? ? ? ? $message = 'Time: '.date('Y-m-d H:i:s').PHP_EOL;
? ? ? ?$message .= 'Info: '.$e->getMessage().PHP_EOL;
? ? ? ? $message .= 'File: '.$e->getFile().PHP_EOL;
? ? ? ? $message .= 'Line: '.$e->getLine().PHP_EOL;
? ? ? ? error_log($message,1,$this->_email);
? ? }
}
抽象通知者角色(Exception_Notify)
class Exception_Notify extends Exception?{
? ? public static $_observer= [];
? ? //關聯觀察者
? ? public static function attach(IObserver $observer){
? ? ? ? self::$_observer[] = $observer;
? ? }
? ? //移除觀察者
? ? public function detach(IObserver $observer){
? ? ? ? foreach(self::$_observer as $k => $v){
? ? ? ? ? ?if($v == $observer){
? ? ? ? ? ? ? unset(self::$_observer[$k]);
? ? ? ? ? }
? ? ? ? }
? ? }
? ? //向觀察者發送通知
? ? public function notify(){
? ? ? ? foreach(self::$_observer as $observer){
? ? ? ? ? ? $observer->update($this);
? ? ? ? }
? ? }
}
具體通知者角色(MyException)
class MyException extends Exception_Notify{
? ? public $_notifyName;
}
測試代碼如下:
// 實例化一個通知者
$my_exception = new MyException();
//添加3個觀察者
$my_exception::attach(new Log_Observer());
$my_exception::attach(new Mail_Observer());
$my_exception::attach(new Log_Observer('test.log'));
try{
? ? throw new MyException('This is a test!!!');
}catch(Exception_Notify $e){
? ? $e->notify();
}
運行結果:
上面的測試代碼將會在同級目錄生成兩個日志文件,一個是默認的error.log還有一個是自定義的test.log;同時會向默認的郵箱273788888@qq.com發送相應的信息。
如對本例中異常的處理不是很理解,可以查看我的上一篇日志:PHP異常處理機制
——《完》——