PHP设计模式之观察者模式

使用场景#

假设项目经理让我们写了一个登陆接口,咔咔擦擦写完了

  • 第二天让我们加入统计登陆次数,然后在后面加代码
  • 第三天让我们判断登陆地区,又在后面加代码
  • 第四天让我们在用户登陆后推送活动,再再后面加代码
  • 第 N 天,这个接口已经杂乱到没人想维护了

我们需要让项目保持高内聚低耦合,就可以用到观察者模式(也不是非要,看需求)

概念#

观察者,观察者,首先要有个被人观察的角色,这是唯一的,然后会有无数个观察者去看她,可以说是一群人在围观一个人,既然有无数个观众,那总得有个东西记录有哪些观察者,那就应该有一个类似于数组一样来储存所有观察者,总结就是一个被观察者,无数个观察者,再有一个容器记录

代码示例#

  • 接口示例
    // 主题接口
    interface Subject{
      public function register(Observer $observer);
      public function notify();
    }
    // 观察者接口
    interface Observer{
      public function watch();
    }
    Subject 就是被观察者,Observer 就是观众,也就是观察者

被观察者#

// 被观察者
class Action implements Subject{
     public $_observers=array();
     public function register(Observer $observer){
         $this->_observers[]=$observer;
     }

     public function notify(){
         foreach ($this->_observers as $observer) {
             $observer->watch();
         }

     }
 }

Action 实现了被观察者接口,他现在就是被观察者,再定义一个 $_observers 数组,他就是记录观众的容器了。
首先实现 register 方法,用它传入一个观察者,然后塞到数组里,再实现 notify () 方法,它会遍历容器数组,执行每个观察者的 watch () 方法。

观察者#

// 观察者
class Cat implements Observer{
     public function watch(){
         echo "Cat watches TV<hr/>";
     }
 }
 class Dog implements Observer{
     public function watch(){
         echo "Dog watches TV<hr/>";
     }
 }
 class People implements Observer{
     public function watch(){
         echo "People watches TV<hr/>";
     }
 }

这里定义了三个观察者,全都实现了 Observer 接口,前面的 Subject 会循环调用每个观察者的 watch () 方法,所以我们需要实现每个观察者的 watch () 方法。

调用#

 // 应用实例
$action=new Action();
$action->register(new Cat());
$action->register(new People());
$action->register(new Dog());
$action->notify();

首先 new 被观察者对象,执行它的 register () 方法,把每个观察者都放入容器数组,最后执行 notify () 方法,通知所有观察者执行自己的方法。

PHP 原生自带的观察者模式#

PHP 有自带的观察者模式

  • splsubject 接口 - 被观察者
  • Observer 接口 - 观察者
  • SplObjectStorage 对象 - 容器

首先我们有一个用户登录类

class user{

  public function login()
  {
      echo '登录完毕'
  }

让他实现 splsubject 接口成为被观察者。

  • 首先在构造函数里,让他 new SplObjectStorag () 对象并赋值到属性上方便后面调用

  • 实现 attach () 方法,用来注册观察者

  • 实现 detach () 方法,用来删除观察者

  • 实现 notify () 方法,用来遍历容器,调用每个观察者的 update 方法 (必须是 update)

  • rewind 方法是容器指针重置到最开始,valid 方法检测容器是否遍历完成并返回布尔,current 方法是获取当前的观察者,next 方法是将指针后移一位

  • 修改 login () 方法,在里面调用 notify () 来通知观察者事件完成了

    class user implements splsubject{
    
      protected $observer = null;
    
      public function __construct()
      {
          $this->observer = new SplObjectStorage();
      }
    
      public function login()
      {
          $this->notify();
          echo '登录完毕';
      }
    
      public function attach(SplObserver $observer)
      {
          $this->observer->attach($observer);
      }
    
      public function detach(SplObserver $observer)
      {
          $this->observer->detach($observer);
      }
    
      public function notify()
      {
          $this->observer->rewind();
          while ($this->observer->valid())
          {
              $observer = $this->observer->current();
              $observer->update($this);
              $this->observer->next();
          }
      }
    }

观察者#

每个观察者实现 SplObserver 接口,并实现 update () 方法

class cat implements SplObserver {

    public function update(SplSubject $subject)
    {
        echo '小猫叫一下';
    }
}
class dog implements SplObserver {
    public function update(SplSubject $subject)
    {
        echo '小狗吼一声';
    }
}

应用#

// 实时观察
$user = new user();
$user->attach(new cat());
$user->attach(new dog());
$user->login();
本作品采用《CC 协议》,转载必须注明作者和本文链接