PHP设计模式 观察者设计模式

概述:
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
主要解决:
将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
模式中的角色:
模式中的角色抽象主题(Subject):
它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

PHP设计模式 观察者设计模式_第1张图片



优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。


使用场景:
1、有多个子类共有的方法,且逻辑相同。
2、重要的、复杂的方法,可以考虑作为模板方法。

典型案例:
1、用户注册(验证邮件,用户信息激活);
2、购物网站下单时邮件/短信通知等


注意事项:
1、PHP中已经有了对观察者模式的支持接口SplObserver,SplSubject
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式

代码示例:

<?php
/*
*角色 被观察者(抽象主题、具体主题);观察者(抽象观察者、具体观察者)
*/

/**
* 抽象主题角色
*/
interface Subject {
    /**
     * 增加一个新的观察者对象
     * @param Observer $observer
     */
    public function attach(Observer $observer);

    /**
     * 删除一个已注册过的观察者对象
     * @param Observer $observer
     */
    public function detach(Observer $observer);

    /**
     * 通知所有注册过的观察者对象
     */
    public function notify();
}

/**
* 具体主题角色
*/
class ConcreteSubject implements Subject {
    private $_observers;
    
    public function __construct() {
        $this->_observers = array();
    }

    /**
     * 增加一个新的观察者对象
     * @param Observer $observer
     */
    public function attach(Observer $observer) {
        return array_push($this->_observers, $observer);
    }

    /**
     * 删除一个已注册过的观察者对象
     * @param Observer $observer
     */
    public function detach(Observer $observer) {
        $index = array_search($observer, $this->_observers);
        if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
            return FALSE;
        }
        unset($this->_observers[$index]);
        return TRUE;
    }

    /**
     * 通知所有注册过的观察者对象
     */
    public function notify() {
        if (!is_array($this->_observers)) {
            return FALSE;
        }
        foreach ($this->_observers as $observer) {
            $observer->update();
        }
        return TRUE;
    }
}

/**
* 抽象观察者角色
*/
interface Observer {
    /**
     * 更新方法
     */
    public function update();
}

/*
*具体观察者
*/
class ConcreteObserver implements Observer {
    /**
     * 观察者的名称
     * @var <type>
     */
    private $_name;
    public function __construct($name) {
        $this->_name = $name;
    }
    /**
     * 更新方法
     */
    public function update() {
        echo 'Observer ', $this->_name, ' has notified.<br />';
    }
}

$subject = new ConcreteSubject();//实例化具体主题类:

$observer1 = new ConcreteObserver('Martin');//添加第一个观察者
$subject->attach($observer1);
echo '<br /> The First notify:<br />';
$subject->notify();


$observer2 = new ConcreteObserver('phppan');// 添加第二个观察者
$subject->attach($observer2);

echo '<br /> The Second notify:<br />';
$subject->notify();

/* 删除第一个观察者 */
$subject->detach($observer1);

echo '<br /> The Third notify:<br />';
$subject->notify();

<?php
/**
  * 用户登陆-诠释观察者模式
  */
class User implements SplSubject {
    //注册观察者
    public $_observers = array();
    public $email = '';

    //动作类型
    CONST OBSERVER_TYPE_REGISTER = 1;//注册
    CONST OBSERVER_TYPE_EDIT = 2;//编辑

    /**
     * 追加观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function attach(SplObserver $observer,$type=''){
        return array_push($this->_observers, $observer);
    }

    /**
     * 去除观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function detach(SplObserver $observer){
        $index = array_search($observer, $this->_observers);
        if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
            return FALSE;
        }
        unset($this->_observers[$index]);
        return TRUE;
    }

    /**
     * 满足条件时通知观察者
     * @param int $type 观察类型
     */
    public function notify(){
        if(!empty($this->_observers)){
            foreach($this->_observers as $observer){
                $observer->update($this);
            }
        }
    }

    /**
     * 添加用户
     * @param str $username 用户名
     * @param str $password 密码
     * @param str $email 邮箱
     * @return bool
     */
    public function addUser(){
        //执行sql
        //数据库插入成功
        $res = true;
        //调用通知观察者
        $this->email = '[email protected]';
        $this->notify();
        return $res;
    }

    /**
     * 用户信息编辑
     * @param str $username 用户名
     * @param str $password 密码
     * @param str $email 邮箱
     * @return bool
     */
    public function editUser(){
        //执行sql
        //数据库更新成功
        $res = true;
        //调用通知观察者
        $this->notify();
        return $res;
    }
}

/**
* 观察者-发送邮件
*/
class Send_Mail implements SplObserver{
    
    protected $title = '';
    protected $content = '';
    
    public function __construct($title,$content){
        $this->title = $title;
        $this->title = $content;
    }
    
    /**
     * 相应被观察者的变更信息
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject){
        $this->sendEmail($subject->email, $this->title, $this->content);
    }

    /**
     *发送邮件
     *@param str $email 邮箱地址
     *@param str $title 邮件标题
     *@param str $content 邮件内容
     */
    public function sendEmail($email, $title, $content){
        //调用邮件接口,发送邮件
        echo "发送主题为{$title},内容为{$content}的邮件到{$email}邮箱";
    }
}
$user = new User();
$sendEmail = new Send_Mail('条件用户','通知添加用户');
$user->attach($sendEmail);
$user->addUser();


<?php
/**
  * 用户登陆-诠释观察者模式
  */
class User implements SplSubject {
    //注册观察者
    public $_observers = array();
    public $email = '';

    //动作类型
    CONST OBSERVER_TYPE_REGISTER = 1;//注册
    CONST OBSERVER_TYPE_EDIT = 2;//编辑

    /**
     * 追加观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function attach(SplObserver $observer, $type=1){
        $this->_observers[$type][] = $observer;
        return true;
    }

    /**
     * 去除观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function detach(SplObserver $observer, $type=1){
        $index = array_search($observer, $this->_observers[$type]);
        if ($index === FALSE || ! array_key_exists($index, $this->_observers[$type])) {
            return FALSE;
        }
        unset($this->_observers[$type][$index]);
        return TRUE;
    }

    /**
     * 满足条件时通知观察者
     * @param int $type 观察类型
     */
    public function notify($type=1){
        if(!empty($this->_observers[$type])){
            foreach($this->_observers[$type] as $observer){
                $observer->update($this);
            }
        }
    }

    /**
     * 添加用户
     * @param str $username 用户名
     * @param str $password 密码
     * @param str $email 邮箱
     * @return bool
     */
    public function addUser(){
        //执行sql
        //数据库插入成功
        $res = true;
        //调用通知观察者
        $this->email = '[email protected]';
        $this->notify(self::OBSERVER_TYPE_REGISTER);
        return $res;
    }

    /**
     * 用户信息编辑
     * @param str $username 用户名
     * @param str $password 密码
     * @param str $email 邮箱
     * @return bool
     */
    public function editUser(){
        //执行sql
        //数据库更新成功
        $res = true;
        //调用通知观察者
        $this->notify(self::OBSERVER_TYPE_EDIT);
        return $res;
    }
}

/**
* 观察者-发送邮件
*/
class Send_Mail implements SplObserver{
    
    protected $title = '';
    protected $content = '';
    
    public function __construct($title,$content){
        $this->title = $title;
        $this->content = $content;
    }
    
    /**
     * 相应被观察者的变更信息
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject){
        $this->sendEmail($subject->email, $this->title, $this->content);
    }

    /**
     *发送邮件
     *@param str $email 邮箱地址
     *@param str $title 邮件标题
     *@param str $content 邮件内容
     */
    public function sendEmail($email, $title, $content){
        //调用邮件接口,发送邮件
        echo "发送主题为{$title},内容为{$content}的邮件到{$email}邮箱";
    }
}
$user = new User();
$sendEmail = new Send_Mail('add User','message add User');
$user->attach($sendEmail,1);
$user->addUser();

你可能感兴趣的:(设计模式,接口,角色,观察者,主题)