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

xhqiang 2019-06-21

前言

知识就是作为观察者所获得的结论,经过科学培训的观察者会为我们提供所有能感知的现实。设计观察者模式是为了让一个对象跟踪某个状态,知道状态何时改变,一旦状态改变,所有订阅对象都能得到通知。如果需要保证一个状态的一致性(比如说:状态触发类的应用),但是这个给定状态可能有多个不同的用户等级,这种情况下观察者模式就很适用,而且很有帮助,各司其职。利用观察者模式可以维护一致性,同时记录创建一个给定的状态的对象个数。

观察者模式很直观。何必让多个对象创建或跟踪一个给定的状态呢?如果由一个对象完成这个工作,然后通知其他可能用到这个状态的对象,这样会合理得多。

使用SPL实现观察者模式

1、可用于观察者设计模式的3个SPL接口/类如下:
● SplSubject
● SplObserver
● SplObjectStorage

1.1、SplSubject

<?php

namespace common\observer;

use Yii;

/**
 * SplSubject.
 *
 */
interface SplSubject
{
    /*SplSubject 接口*/
    public function attach (SplObserver $observer);
    public function detach (SplObserver $observer);
    public function notify ();
}

注意这个SplSubject 接口指定attach()和detach()方法参数中$observer的数据类型必须是一个SplObserver 对象。

1.2、SplObserver

SplObserver 接口只有一个update()方法,如下所示:

<?php

namespace common\observer;

use Yii;

/**
 * SplObserver
 *
 */
interface SplObserver
{
    /*更新方法*/
    public function update (SplSubject $subject);
}

update()方法对于观察者模式至关重要,因为它会得到Subject状态的最新变化,并交给观察者实例。

1.3、SplObjectStorage

SplObjectStorage 类与观察者设计模式没有内在的关系,不过通过它其内置的attach()和detach()方法可以很方便的将观察者实例与一个主题实例相关联以及解除关联。

<?php

namespace common\observer;

use Yii;

/**
 * SplObjectStorage 存储对象的类
 * 
 * @property string $storage 存储对象
 * 
 */
class SplObjectStorage
{    
    public $storage;

    //增加方法
    
    public function attach(SplObserver $observer)
    { 
        $this->storage[] = $observer;
    }

    //删除方法

    public function detach(SplObserver $observer)
    {  
        if(is_int($idx = array_search($observer, $this->storage)))
        {
            unset($this->storage[$idx]);
        }
    }
}

注释:如果直接用PHP类库的SplObjectStorage类的话,1.3可以不看了,只需要把1.4里面的$this->observers = new SplObjectStorage();修改为$this->observers = new \SplObjectStorage();(原因可以去了下PHP的解命名空间和文件加载机制。参考资料),notify方法里的$this->observers->storage修改为$this->observers即可。

1.4、SPL具体主题

SplSubject接口不包括获取方法和设置方法,不过这也是观察者设计模式中的一部分,所以需要增加获取方法和设置方法。设置方法setData()包含一个参数,这是要增加的任何类型的数据。获取方法getData()存储当前的主题状态,由具体观察者用来更新观察者数据。
另外还增加了setObservers()方法。并不是在构造函数中设置SplObjectStorage()实例,也没有在setData()方法中设置观察者实例,这里实现了一个单独的setObservers()方法,可以提供更轻松的耦合,并允许有多组观察者。

<?php

namespace common\observer;

use Yii;
 
//ConcreteSubject
 
class ConcreteSubject implements SplSubject
{
 
    private $observers,$data;
 
    public function setObservers()
    {
 
        //sqlObjectStorage 专门用来存储对象的类
 
        $this->observers = new SplObjectStorage();


    }

    //添加观察者
 
    public function attach(SplObserver $observer)
    {
 
        $this->observers->attach($observer);
 
    }
 
    //剔除观察者
 
    public function detach(SplObserver $observer)
    {
 
        $this->observers->detach($observer);
 
    }
 
    //通知notify

    public function notify()
    {  
        foreach ($this->observers->storage as $key =>$observer) { 
            $observer->update($this);
        }
 
    }

    //设置方法

    public function setData($dataNow)
    { 
        $this->data=$dataNow;
    }

    //获取方法

    public function getData()
    {   
        return $this->data;
    }
 
}
 
?>

1.5、SPL具体观察者

用于实现更新函数来更新关联的观察者实例。

<?php


namespace common\observer;

use Yii;
 
//ConcreteObserver
 
class ConcreteObserver implements SplObserver
{
 
    public function update(SplSubject $subject)
    {  
        echo $subject->getData()."</br>";
    }
 
}
 
?>

(附加的普通用户具体观察者)

<?php


namespace common\observer;

use Yii;
 
//Userbservers
 
class UserObserver implements SplObserver
{
 
    public function update(SplSubject $subject)
    {
 
        echo '我是普通用户,请给我对应的普通用户服务';
 
    }
 
}
?>

1.6、SPL客户

“SPL”Client 类只是一个标准客户。这个客户按照SPL接口向具体主题和观察者发出多个请求,不过自己并没有实现SPL类和接口。

<?php


namespace common\observer;

use Yii;
 
//Client

class Client
{
 
    public function __construct()
    {
        echo "<h3> 创造新的具体观察者,新的具体主体:</h3>";

        $ob1 = new ConcreteObserver();
        $ob2 = new ConcreteObserver();
        $ob3 = new ConcreteObserver();

        $Subject = new ConcreteSubject();
        $Subject->setObservers();
        $Subject->setData("这是你的数据!");
        $Subject->attach($ob1);
        $Subject->attach($ob2);
        $Subject->attach($ob3);

        $Subject->notify();

        echo "<h3>删除ob3,结果是ob1和ob2的通知:</h3>";
        $Subject->detach($ob3);
        $Subject->notify();

        echo "<h3>剩余的数据和附加数据还有删除ob2,结果是ob1和ob3的通知:</h3>";
        $Subject->setData("更多的数据,只有ob1和ob3是需要的");
        $Subject->attach($ob3);
        $Subject->detach($ob2);
        $Subject->notify();
  
        echo "<h3>剩余数据和附加新数据,结果是ob1和ob3还有“新数据”的通知:</h3>";
        $Subject->attach(new \common\observer\UserObserver);
        $Subject->notify();
    }
 
}
 
?>

1.7、Client的调用输出如下:

调用:$worker=new commonobserverClient();

输出:
PHP设计模式——观察者模式

总结分析

这里SplObjectStorage类是我自己写的,贴出来分享一下。PHP5.1.0以及更高的版本有很多特性,其中之一就是提供了一组可以用于观察者的设计模式的接口。可以研究一下怎么使用,SplObserver接口以及SplSubject和SplObjectStorage接口,利用这些接口,构建观察者模式简直易如反掌。“SPL”是标准PHP类库(Standard PHP Library)的简写,这个库中包括一组解决标准问题的接口和类。参考手册

相关资料

关于观察者模式
利用 SPL 快速实现 Observer 设计模式
PHP SPL标准库之数据结构对象容器(SplObjectStorage)

相关推荐