PHP设计模式之观察者模式(Observer)详细介绍和代码实例


Posted in PHP onApril 08, 2014

【意图】

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新【GOF95】 又称为发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听(Source-Listener)模式、或从属者(Dependents)模式

【观察者模式结构图】

PHP设计模式之观察者模式(Observer)详细介绍和代码实例

【观察者模式中主要角色】

1.抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。 抽象主题提供了增加和删除观察者对象的接口。
2.抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
3.具体主题(ConcreteSubject)角色:存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
4.具体观察者(ConcretedObserver)角色:存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。

【观察者模式的优点和缺点】

观察者模式的优点:

1.观察者和主题之间的耦合度较小;
2.支持广播通信;

观察者模式的缺点:

由于观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。这可能会引起意外的更新。

【观察者模式适用场景】

当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。

【观察者模式与其它模式】

1.中介者模式(Mediator):通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者。
2.单例模式(singleton模式):ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问的。

【观察者模式PHP示例】

<?php
/**
* 观察者模式
* @package design pattern
*/
/**
* 抽象主题角色
*/
interface Subject {
    /**
     * 增加一个新的观察者对象
     * @param Observer $observer
     */
    public function attach(Observer $observer);
    /**
     * 删除一个已注册过的观察者对象
     * @param Observer $observer
     */
    public function detach(Observer $observer);
    /**
     * 通知所有注册过的观察者对象
     */
    public function notifyObservers();
}
/**
* 具体主题角色
*/
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 notifyObservers() {
        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->notifyObservers();
/* 添加第二个观察者 */
$observer2 = new ConcreteObserver('phppan');
$subject->attach($observer2);
echo '<br /> The Second notify:<br />';
$subject->notifyObservers();
/* 删除第一个观察者 */
$subject->detach($observer1);
echo '<br /> The Third notify:<br />';
$subject->notifyObservers();
具体案例:
<?php
 /**  
  * 3.1php设计模式-观测者模式  
  * 3.1.1概念:其实观察者模式这是一种较为容易去理解的一种模式吧,它是一种事件系统,意味  
  *          着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,  
  *          观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间
  *          紧密耦合的另一种方法
  * 3.1.2关键点:
  *        1.被观察者->追加观察者;->一处观察者;->满足条件时通知观察者;->观察条件
  *        2.观察者 ->接受观察方法
  * 3.1.3缺点:
  * 3.1.4观察者模式在PHP中的应用场合:在web开发中观察者应用的方面很多
  *        典型的:用户注册(验证邮件,用户信息激活),购物网站下单时邮件/短信通知等
  * 3.1.5php内部的支持
  *        SplSubject 接口,它代表着被观察的对象,
  *        其结构:
  *        interface SplSubject
  *        {
  *            public function attach(SplObserver $observer);
  *            public function detach(SplObserver $observer);
  *            public function notify();
  *        }
  *        SplObserver 接口,它代表着充当观察者的对象,
  *        其结构:
  *        interface SplObserver
  *        {   
  *            public function update(SplSubject $subject);
  *        }
  */
 /**
  * 用户登陆-诠释观察者模式
  */
class User implements SplSubject {
    //注册观察者
    public $observers = array();
    //动作类型
    CONST OBSERVER_TYPE_REGISTER = 1;//注册
    CONST OBSERVER_TYPE_EDIT = 2;//编辑
    /**
     * 追加观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function attach(SplObserver $observer, $type)
    {
        $this->observers[$type][] = $observer;
    }
    /**
     * 去除观察者
     * @param SplObserver $observer 观察者
     * @param int $type 观察类型
     */
    public function detach(SplObserver $observer, $type)
    {
        if($idx = array_search($observer, $this->observers[$type], true))
        {
            unset($this->observers[$type][$idx]);
        }
    }
    /**
     * 满足条件时通知观察者
     * @param int $type 观察类型
     */
    public function notify($type)
    {
        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->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
 {
    /**
     * 相应被观察者的变更信息
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject)
    {
        $this->sendMail($subject->email, $title, $content);
    }
    /**
     *发送邮件
     *@param str $email 邮箱地址
     *@param str $title 邮件标题
     *@param str $content 邮件内容
     */
    public function sendEmail($email, $title, $content)
    {
        //调用邮件接口,发送邮件
    }
}
?>
PHP 相关文章推荐
动态网站web开发 PHP、ASP还是ASP.NET
Oct 09 PHP
php中sprintf与printf函数用法区别解析
Feb 17 PHP
php实现的zip文件内容比较类
Sep 24 PHP
php使用Jpgraph绘制柱形图的方法
Jun 10 PHP
php读取qqwry.dat ip地址定位文件的类实例代码
Nov 15 PHP
centos 7.2下搭建LNMP环境教程
Nov 20 PHP
详解PHP发送邮件知识点
May 06 PHP
PHP如何根据文件头检测文件类型实例代码
Oct 14 PHP
php和vue配合使用技巧和方法
May 09 PHP
漂亮的thinkphp 跳转页封装示例
Oct 16 PHP
PHP Trait功能与用法实例分析
Jun 03 PHP
聊聊 PHP 8 新特性 Attributes
Aug 19 PHP
关于PHP的curl开启问题探讨
Apr 08 #PHP
PHP中Session引起的脚本阻塞问题解决办法
Apr 08 #PHP
PHP中比较两个字符串找出第一个不同字符位置例子
Apr 08 #PHP
PHP用星号隐藏部份用户名、身份证、IP、手机号等实例
Apr 08 #PHP
php实现telnet功能示例
Apr 08 #PHP
C#使用PHP服务端的Web Service通信实例
Apr 08 #PHP
php实现水仙花数的4个示例分享
Apr 08 #PHP
You might like
PHP连接MySQL查询结果中文显示乱码解决方法
2013/10/25 PHP
PHP输出两个数字中间有多少个回文数的方法
2015/03/23 PHP
PHP编程实现csv文件导入mysql数据库的方法
2017/04/29 PHP
高性能Javascript笔记 数据的存储与访问性能优化
2012/08/02 Javascript
Javascript模块化编程(一)AMD规范(规范使用模块)
2013/01/17 Javascript
引入JS文件IE6报语法错误或缺少对象问题的解决方法
2014/01/09 Javascript
处理文本部分内容的TextRange对象应用实例
2014/07/29 Javascript
JavaScript字符串对象replace方法实例(用于字符串替换或正则替换)
2014/10/16 Javascript
JavaScript设计模式之原型模式(Object.create与prototype)介绍
2014/12/28 Javascript
JavaScript实现的链表数据结构实例
2015/04/02 Javascript
javascript实现图片自动和可控的轮播切换特效
2015/04/13 Javascript
基于Css3和JQuery实现打字机效果
2015/08/11 Javascript
JQuery实现级联下拉框效果实例讲解
2015/09/17 Javascript
基于javascript实现彩票随机数生成(升级版)
2020/04/17 Javascript
jQuery form插件之formDdata参数校验表单及验证后提交
2016/01/23 Javascript
基于jQuery实现收缩展开功能
2016/03/18 Javascript
javaScript事件学习小结(四)event的公共成员(属性和方法)
2016/06/09 Javascript
关于Jquery中的bind(),on()绑定事件方式总结
2016/10/26 Javascript
JavaScript中${pageContext.request.contextPath}取值问题及解决方案
2016/12/08 Javascript
JS简单实现表格排序功能示例
2016/12/20 Javascript
Bootstrap的popover(弹出框)2秒后定时消失的实现代码
2017/02/27 Javascript
vue使用keep-alive实现数据缓存不刷新
2017/10/21 Javascript
小程序开发踩坑:页面窗口定位(相对于浏览器定位)(推荐)
2019/04/25 Javascript
Vue.js 中制作自定义选择组件的代码附演示demo
2020/02/28 Javascript
vue addRoutes路由动态加载操作
2020/08/04 Javascript
node使用async_hooks模块进行请求追踪
2021/01/28 Javascript
python求pi的方法
2014/10/08 Python
Python中线程编程之threading模块的使用详解
2015/06/23 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
2016/08/30 Python
解释一下Windows的消息机制
2014/01/30 面试题
初始化了一个没有run()方法的线程类,是否会出错?
2014/03/27 面试题
优秀求职自荐信怎样写
2013/12/18 职场文书
工业学校毕业生自荐信范文
2014/01/03 职场文书
安全责任书范文
2014/03/12 职场文书
解决ObjectMapper.convertValue() 遇到的一些问题
2021/06/30 Java/Android
Go中使用gjson来操作JSON数据的实现
2022/08/14 Golang