php设计模式之观察者模式

理解观察者模式

观察者模式定义了对象之间一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

不知道大家有没有订杂志的经历,理解了订杂志的应用就能理解观察者模式。想想看,如果我们不去订阅杂志,那么我们想要看最新的杂志,就需要主动去商场或书店去看一看,运气好会有最新杂志买,运气不好的话就会空手而归。但如果我们去订阅了该杂志,那么我们就不需要去商场或书店了,只要有新的杂志,邮局就会主动给我们送过里。但我们取消订阅了,那我们就不在订阅者的名单上,邮局就不会给我们送上来。

观察者模式里有两种角色:发布者、订阅者

发布者需要提供的功能有:订阅、取消订阅、当有新的状态时通知订阅者。当有新的状态通知订阅者后,订阅者会进行相关操作。

应用场景及缺点

当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。

它的缺点是,通知的顺序无法保证。

实现观察者模式

首先我们看一个应用场景:当系统有新的用户注册时我们需要:

  • 向用户发送信息通知
  • 更新总用户数量
  • 向用户赠送一张优惠券

UML类图

代码实现

PHP 已经定义了 2 个接口用于快速实现观察者模式:SplObserver 和 SplSubject。这里我们不用php提供的接口,自己来完成。

首先定义Observer接口

interface Observer
{
   public function update ($userId);
}

然后是定义subject接口:

interface Subject
{
   public function register (Observer $o);
   public function remove (Observer $o);
   public function notify ($info = null);
}

再实现具体的发布者:

class UserRegisterSubject implements Subject
{
   private $observers = [];

   // 订阅
   public function register(Observer $o)
  {
       if (!in_array($o, $this->observers)) {
           $this->observers[] = $o;
      }
  }

   // 取消订阅
   public function remove(Observer $o)
  {
       if (($k = array_search($o, $this->observers)) >= 0) {
           unset($this->observers[$k]);
      }
  }

   // 通知订阅者
   public function notify($info = null)
  {
       foreach ($this->observers as $o) {
           $o->update($info);
      }
  }

   // 当有新用户注册时,触发notify方法
   public function userAdd ($userId)
  {
       $this->notify($userId);
  }
}

最后是订阅者的代码实现:

class Quan implements Observer
{
   public function update($userId)
  {
       echo "向用户{$userId}发送一张新人优惠券".PHP_EOL;
  }
}

class SendMsg implements Observer
{
   public function update($userId)
  {
       echo "向用户{$userId}发送站内信".PHP_EOL;
  }
}

class UserCnt implements Observer
{
   public function update($userId)
  {
       echo '更新用户总数量'.PHP_EOL;
  }
}

使用php已定义的接口快速实现

发布者代码

class UserRegisterSubject implements \SplSubject
{
   private $observers = [];
   private $userId = 0;

   public function attach (\SplObserver $o)
  {
       if (!in_array($o, $this->observers)) {
           $this->observers[] = $o;
      }
  }

   public function detach(\SplObserver $o)
  {
       if (($k = array_search($o, $this->observers)) >= 0) {
           unset($this->observers[$k]);
      }
  }

   public function notify()
  {
       foreach ($this->observers as $o) {
           $o->update($this);
      }
  }

   // 当有新用户注册时,触发notify方法
   public function userAdd ($userId)
  {
       $this->userId = $userId;
       $this->notify();
  }

   public function getter ($key)
  {
       if (isset($this->$key)) {
           return $this->$key;
      }
  }
}

订阅者代码:


class Quan implements \SplObserver
{
   public function update(\SplSubject $sub)
  {
       echo "向用户{$sub->getter('userId')}发送一张新人优惠券".PHP_EOL;
  }
}

class UserCnt implements \SplObserver
{
   public function update(\SplSubject $sub)
  {
       echo '更新用户总数量'.PHP_EOL;
  }
}

class SendMsg implements \SplObserver
{
   public function update(\SplSubject $sub)
  {
       echo "向用户{$sub->getter('userId')}发送站内信".PHP_EOL;
  }
}