PHP软件开发:设计模式|观察者模式(Observer)解析与应用

2020-08-2021:45:59后端程序开发Comments1,544 views字数 3716阅读模式

使用场景

假设项目经理让我们写了一个登陆接口,咔咔擦擦写完了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

  • 第二天让我们加入统计登陆次数,然后在后面加代码 【观察者模式结构图】
PHP软件开发:设计模式|观察者模式(Observer)解析与应用

概念

(Observer),首先要有一个被观察的角色,但ta是【唯一的】。虽然"表演者"只有一个但是"观众"有很多,既一群"人"围观一个"人"。既然有无数个观察者,那么我们需要知道都有哪一些"人"。所以我们需要一个“容器”来记录这些"人",一个类似于数组一样来储存所有观察者的容器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

一个"演员"(被观察者),一群"观众"(观察者),一台"摄影机"(记录容器)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

★[我的php高级学习交流社群「点击」]管理准备好的社群专属资料:BAT等一线大厂进阶知识体系(相关学习资料以及笔面试题)以及不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

【观察者模式中主要角色】文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

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

【使用场景】文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

假设项目经理让我们写了一个登陆接口,咔咔擦擦写完了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

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

我们需要让项目保持高内聚低耦合,就可以用到观察者模式(非必须,看需求)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

【观察者模式与其它模式】文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

1.【中介者模式】(Mediator):通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者。
2.【单例模式】(singleton模式):ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

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

我们需要让项目保持高内聚低耦合,就可以用到观察者模式(也不是非要,看需求)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

概念

观察者,观察者,首先要有个被人观察的角色,这是唯一的,然后会有无数个观察者去看她,可以说是一群人在围观一个人,既然有无数个观众,那总得有个东西记录有哪些观察者,那就应该有一个类似于数组一样来储存所有观察者,总结就是一个被观察者,无数个观察者,再有一个容器记录文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

我的php高级学习交流社群「点击」管理准备好的社群专属资料:BAT等一线大厂进阶知识体系(相关学习资料以及笔面试题)以及不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

代码示例

  • 接口示例
// 主题接口
interface Subject{
    public function register(Observer $observer);
    public function notify();
}
// 观察者接口
interface Observer{
    public function watch();
}
复制代码

Subject就是被观察者,Observer就是观众,也就是观察者文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

被观察者

// 被观察者
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数组,他就是记录观众的容器了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

首先实现register方法,用它传入一个观察者,然后塞到数组里,再实现notify()方法,它会遍历容器数组,执行每个观察者的watch()方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

观察者

// 观察者
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()方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

调用

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

首先new被观察者对象,执行它的register()方法,把每个观察者都放入容器数组,最后执行notify()方法,通知所有观察者执行自己的方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

PHP原生自带的观察者模式

PHP有自带的观察者模式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

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

首先我们有一个用户登录类文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

class user{

  public function login()
  {
      echo '登录完毕'
  }
复制代码

让他实现splsubject接口成为被观察者。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

  • 首先在构造函数里,让他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()方法文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

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();

作者:八重樱
来源:掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/20097.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/20097.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定