【看一下观察者模式结构图】

PHP观察者模式(Observer)解析与应用 - 图1

概念

(Observer),首先要有一个被观察的角色,但它是【唯一的】。虽然“表演者”只有一个但是“观众”有很多,即一群“人”围观一个“人”。既然有无数个观察者,那么我们需要知道都有哪一些“人”。所以我们需要一个“容器”来记录这些“人”,一个类似于数组一样来储存所有观察者的容器。

一个“演员”(被观察者),一群“观众”(观察者)。一台“摄影机”(记录容器)

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

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

【使用场景】

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

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

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

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

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

代码示例

接口示例

  1. // 主题接口
  2. interface Subject{
  3. public function register(Observer $observer);
  4. public function notify();
  5. }
  6. // 观察者接口
  7. interface Observer{
  8. public function watch();
  9. }

Subject就是被观察者,Observer就是观众,也就是观察者

被观察者

  1. // 被观察者
  2. class Action implements Subject{
  3. public $_observers=array();
  4. public function register(Observer $observer){
  5. $this->_observers[]=$observer;
  6. }
  7. public function notify(){
  8. foreach ($this->_observers as $observer) {
  9. $observer->watch();
  10. }
  11. }
  12. }

Action 实现了被观察者接口,他现在就是被观察者,再定义一个$_observers 数组,他就是记录观众的容器了。

首先实现register方法,用它传入一个观察者,然后塞到数组里,再实现notify()方法,他会遍历容器数组,执行每个观察者的watch()方法。

观察者

  1. // 观察者
  2. class Cat implements Observer{
  3. public function watch(){
  4. echo "Cat watches TV<hr/>";
  5. }
  6. }
  7. class Dog implements Observer{
  8. public function watch(){
  9. echo "Dog watches TV<hr/>";
  10. }
  11. }
  12. class People implements Observer{
  13. public function watch(){
  14. echo "People watches TV<hr/>";
  15. }
  16. }

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

调用

  1. // 应用实例
  2. $action=new Action();
  3. $action->register(new Cat());
  4. $action->register(new People());
  5. $action->register(new Dog());
  6. $action->notify();

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

PHP原生自带的观察者模式

PHP有自带的观察者模式

  1. splsubject接口 - 被观察者
  2. Observer接口 - 观察者
  3. SplObjectStorage对象 - 容器

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

  1. class user{
  2. public function login()
  3. {
  4. echo '登录完毕'
  5. }

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

  1. 首先在构造函数里,让他new SP了ObjectStorag()对象并赋值到属性上方便后面调用
  2. 实现attach()方法,用来注册观察者
  3. 实现detach()方法,用来删除观察者
  4. 实现notify()方法,用来遍历容器,调用每个观察者的update方法(必须是update)
  5. rewind方法是容器指针重置到最开始,valid方法检测容器是否遍历完成并返回布尔,current方法是获取当前的观察者,next方法是将指针后移一位
  6. 修改login()方法,在里面调用notify()来通知观察者事件完成了

    1. class user implements splsubject{
    2. protected $observer = null;
    3. public function __construct()
    4. {
    5. $this->observer = new SplObjectStorage();
    6. }
    7. public function login()
    8. {
    9. $this->notify();
    10. echo '登录完毕';
    11. }
    12. public function attach(SplObserver $observer)
    13. {
    14. $this->observer->attach($observer);
    15. }
    16. public function detach(SplObserver $observer)
    17. {
    18. $this->observer->detach($observer);
    19. }
    20. public function notify()
    21. {
    22. $this->observer->rewind();
    23. while ($this->observer->valid())
    24. {
    25. $observer = $this->observer->current();
    26. $observer->update($this);
    27. $this->observer->next();
    28. }
    29. }
    30. }

    观察者

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

    1. class cat implements SplObserver {
    2. public function update(SplSubject $subject)
    3. {
    4. echo '小猫叫一下';
    5. }
    6. }
    7. class dog implements SplObserver {
    8. public function update(SplSubject $subject)
    9. {
    10. echo '小狗吼一声';
    11. }
    12. }

    应用

    1. // 实时观察
    2. $user = new user();
    3. $user->attach(new cat());
    4. $user->attach(new dog());
    5. $user->login();