观察者模式的核心是把客户元素(观察者)从一个中心类(主体)中分离出来。当主体知道时间发生时,观察者需要被通知到。同时,不需要主体和观察者之间进行硬编码。
《设计模式》中对Observer模式的意图的描述是:“定义对象间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都能到得到通知并自动更新”
Observer模式用到的几条面向对象的原则也值得关注:
1.对象自我负责:Observer对象有多种,但是都从Subject对象手机所需信息,并自己完成相应的操作。
2.抽象类(接口):Observer类(接口)表示了“需要通知的对象”这一概念,它为目标提供了一个通知observer的公共接口。
3.多态封装:Subject并不知道与那种观察者通信。实际上,observer类封装了各种特定的observer.这以为这如果我在未来有新的observer,不需要更改subject类。
《深入php面向对象模式与实践》给给出的例子:
假设我们有一个login类处理用户的登陆信息,当用户登录时,根据用户的登录状态,做相应的动作。如果不对login对象和相应的动作做硬编码的话。observer模式是一个极佳的选择。
Observable 接口(主体):
interface Observable{
function attach(Observer $observer);
function detach(Observer $observer);
function notify();
}
具体的Login(登录主体):
class login implements Observable{
const LOGIN_ACCESS = 1;
const LOGIN_WRONG_PASS = 2;
const LOGIN_USER_UNKNOWN = 3;
private $status = array();
private $observers;
public function __construct(){
$this->observers = array();
}
public function attach(Observer $observer){
$this->observers[] = $observer;
}
public function detach(Observer $observer){
$newObservers = array();
foreach($this->observers as $obs){
if($obs != $observer){
$newObservers[] = $obs;
}
}
$this->observers = $newObservers;
}
private function setStatus($status,$user,$ip){
$this->status = array($status,$user,$ip);
}
public function getStatus(){
return $this->status;
}
public function notify(){
foreach($this->observers as $obs){
$obs->update($this);
}
}
public function handlerLogin( $user,$pass,$ip){
switch(rand(1,3)){
case 1:
$this->setStatus(Self::LOGIN_ACCESS,$user,$ip);
$ret = true;break;
case 2:
$this->setStatus(Self::LOGIN_WRONG_PASS,$user,$ip);
$ret = false;break;
case 3:
$this->setStatus(Self::LOGIN_USER_UNKNOWN,$user,$ip);
$ret = true;break;
}
}
}
观察者接口:
interface Observer{
function update(Observable $observable);
}
抽象的login观察者类:
abstract class LoginObserver implements Observer{
private $login;
public function __construct(Login $login){
$this->login = $login;
$login->attach($this);
}
public function update(Observable $observable){
if($observable == $this->login){
$this->doUpdate($observable);
}
}
abstract private function doUpdate( Login $login );
}
具体的观察者类:
//观察者1:登录失败时候发送邮件
class SecurityMonitor extends LoginObserver{
private function doUpdate( Login $login ){
$status = $login->getStatus();
if($status[0] == Login::LOGIN_WRONG_PASS){
print __CLASS__."\tsending mail to sysadmin";
}
}
}
//观察者2:记录登录日志
class GeneralLogger extends LoginObserver{
private function doUpdate(Login $login){
$status = $login->getStatus();
//LOG
print __CLASS__."[status]:{$status}\tadd login data to log";
}
}
//观察者3:设置cookie
class PartnerShipTool extends LoginObserver{
private function doUpdate(Login $login){
$status = $login->getStatus();
//COOKIE
print __CLASS__."[Cookie]:{$status}\t set cookie to a list";
}
}
各个类之间的类图关系如下图所示:
PHP内置的SPL扩展提供了对观察者模式的原生态支持。其中的观察者有三个元素组成,SplObserver,SplSubject,SplStorage.其中SplObserver,SplSubject都是接口。SplStorage是个工具类,用于更好滴储存对象和删除对象。
有SPL的支持,实现观察者模式更加方便一些了。