欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

PHP设计模式之观察者模式

程序员文章站 2022-06-06 10:20:27
...

概念

观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。

当一个对象状态发生改变后,会影响到其他几个对象的改变,这时候可以用观察者模式。

观察者模式符合接口隔离原则,实现了对象之间的松散耦合。

别名

发布-订阅<Subscribe>模式

模型-视图<View>模式

源-收听者<Listener>模式

从属者模式

角色

抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

UML图

PHP设计模式之观察者模式

代码

示例代码

在PHP SPL中已经提供SplSubject和SqlOberver接口,源码如下:

/**
 * The <b>SplSubject</b> interface is used alongside
 * <b>SplObserver</b> to implement the Observer Design Pattern.
 * @link http://php.net/manual/en/class.splsubject.php
 */
interface SplSubject  {

        /**
         * Attach an SplObserver
         * @link http://php.net/manual/en/splsubject.attach.php
         * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to attach.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function attach (SplObserver $observer);

        /**
         * Detach an observer
         * @link http://php.net/manual/en/splsubject.detach.php
         * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to detach.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function detach (SplObserver $observer);

        /**
         * Notify an observer
         * @link http://php.net/manual/en/splsubject.notify.php
         * @return void 
         * @since 5.1.0
         */
        public function notify ();

}

/**
 * The <b>SplObserver</b> interface is used alongside
 * <b>SplSubject</b> to implement the Observer Design Pattern.
 * @link http://php.net/manual/en/class.splobserver.php
 */
interface SplObserver  {

        /**
         * Receive update from subject
         * @link http://php.net/manual/en/splobserver.update.php
         * @param SplSubject $subject <p>
     * The <b>SplSubject</b> notifying the observer of an update.
         * </p>
         * @return void 
         * @since 5.1.0
         */
        public function update (SplSubject $subject);

}

下面我们根据这两个spl接口,写自己的代码:

<?php
header('Content-type:text/html;charset=utf-8');

/**
 * Class Subject 主题
 */
class Subject implements SplSubject
{
    private $_observers = [];

    /**
     * 实现添加观察者方法
     *
     * @param SplObserver $observer
     */
    public function attach(SplObserver $observer)
    {
        if (!in_array($observer, $this->_observers)) {
            $this->_observers[] = $observer;
        }
    }

    /**
     * 实现移除观察者方法
     *
     * @param SplObserver $observer
     */
    public function detach(SplObserver $observer)
    {
        if (false !== ($index = array_search($observer, $this->_observers))) {
            unset($this->_observers[$index]);
        }
    }

    /**
     * 实现提示信息方法
     */
    public function notify()
    {
        foreach ($this->_observers as $observer) {
            $observer->update($this);
        }
    }

    /**
     * 设置数量
     *
     * @param $count
     */
    public function setCount($count)
    {
        echo "数据量加" . $count . '<br>';
    }

    /**
     * 设置积分
     *
     * @param $integral
     */
    public function setIntegral($integral)
    {
        echo "积分量加" . $integral . '<br>';
    }

}

/**
 * Class Observer1 观察者一
 */
class Observer1 implements SplObserver
{
    public function update(SplSubject $subject)
    {
        $subject->setCount(10);
    }
}

/**
 * Class Observer2 观察者二
 */
class Observer2 implements SplObserver
{
    public function update(SplSubject $subject)
    {
        $subject->setIntegral(10);
    }
}


/**
 * Class Client 客户端
 */
class Client
{
    /**
     * 测试方法
     */
    public static function test()
    {
        // 初始化主题
        $subject = new Subject();
        // 初始化观察者一
        $observer1 = new Observer1();
        // 初始化观察者二
        $observer2 = new Observer2();
        // 添加观察者一
        $subject->attach($observer1);
        // 添加观察者二
        $subject->attach($observer2);
        // 消息提示
        $subject->notify();//输出:数据量加1 积分量加10
        // 移除观察者一
        $subject->detach($observer1);
        // 消息提示
        $subject->notify();//输出:数据量加1 积分量加10 积分量加10
    }
}

// 执行测试
Client::test();

运行结果

数据量加10
积分量加10
积分量加10

优点和缺点

优点

观察者和主题之间的耦合度较小;

支持广播通信;

缺点

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

适用场景

当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。

当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。

当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。

相关标签: PHP