Laravel核心代码学习 -- 观察者模式

观察者模式

Laravel的Event事件系统提供了一个简单的观察者模式实现,可以订阅和监听应用中发生的各类事件,在PHP的标准库(SPL)里甚至提供了三个接口SplSubject, SplObserver, SplObjectStorage来让开发者更容易地实现观察者模式,不过我仍是想脱离SPL提供的接口和特定编程语言来讲一下如何经过面向对象程序设计来实现观察者模式,示例是PHP代码不过用其余面向对象语言实现起来也是同样的。git

模式定义

观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆获得通知并被自动更新。观察者模式又叫作发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。github

观察者模式的核心在于Subject和Observer接口,Subject(主题目标)包含一个给定的状态,观察者“订阅”这个主题,将主题的当前状态通知观察者,每次给定状态改变时全部观察者都会获得通知。编程

发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标能够对应多个观察者,并且这些观察者之间没有相互联系,能够根据须要增长和删除观察者,使得系统更易于扩展。bash

模式结构说明

观察者模式UML

  • Subject 目标抽象类
  • ConcreteSubject 具体目标
  • Observer 观察者抽象类
  • ConcreteObserver 具体观察者

应用举例

好比在设置用户(主题)的状态后要分别发送当前的状态描述信息给用户的邮箱和手机,咱们能够使用两个观察者订阅用户的状态,一旦设置状态后主题就会通知的订阅了状态改变的观察者,在两个观察者里面咱们能够分别来实现发送邮件信息和短信信息的功能。编程语言

  1. 抽象目标类
abstract class Subject
{
    protected $stateNow;
    protected $observers = [];

    public function attach(Observer $observer)
    {
        array_push($this->observers, $observer);
    }

    public function detach(Observer $ob)
    {
        $pos = 0;
        foreach ($this->observers as $viewer) {
            if ($viewer == $ob) {
                array_splice($this->observers, $pos, 1);
            }
            $pos++;
        }
    }

    public function notify()
    {
        foreach ($this->observers as $viewer) {
            $viewer->update($this);
        }
    }
}
复制代码

在抽象类中attach detachnotify都是具体方法,这些是继承才能使用的方法,将由Subject的子类使用。学习

  1. 具体目标类
class ConcreteSubject extends Subject
{
    public function setState($state) 
    {
        $this->stateNow = $state;
        $this->notify();
    }

    public function getState()
    {
        return $this->stateNow;
    }
}
复制代码
  1. 抽象观察者
abstract class Observer
{
    abstract public function update(Subject $subject);
}
复制代码

在抽象观察者中,抽象方法update等待子类为它提供一个特定的实现。ui

  1. 具体观察者
class ConcreteObserverDT extends Observer
{
    private $currentState;

    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();

        echo '<div style="font-size:10px;">'. $this->currentState .'</div>';
    }
}

class ConcreteObserverPhone extends Observer
{
    private $currentState;

    public function update(Subject $subject)
    {
        $this->currentState = $subject->getState();

        echo '<div style="font-size:20px;">'. $this->currentState .'</div>';
    }
}
复制代码

在例子中为了理解起来简单,咱们只是根据不一样的客户端设置了不一样的内容样式,实际应用中能够真正的调用邮件和短信服务来发送信息。this

  1. 使用观察者模式
class Client 
{
    public function __construct()
    {
        $sub = new ConcreteSubject();

        $obDT = new ConcreteObserverDT();
        $obPhone = new ConcreteObserverPhone();

        $sub->attach($obDT);
        $sub->attach($obPhone);
        $sub->setState('Hello World');
    }
}

$worker = new Client();
复制代码

什么时候使用观察者模式

  • 一个对象的改变将致使其余一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,能够下降对象之间的耦合度。
  • 一个对象必须通知其余对象,而并不知道这些对象是谁。
  • 基于事件触发机制来解耦复杂逻辑时,从整个逻辑的不一样关键点抽象出不一样的事件,主流程只须要关心最核心的逻辑并能正确地触发事件(Subject),其他相关功能实现由观察者或者叫订阅者来完成。

总结

  • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知全部观察者对象,使它们可以自动更新。spa

  • 模式包含四个角色:目标又称为主题,它是指被观察的对象;具体目标是目标类的子类,一般它包含有常常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;观察者将对观察目标的改变作出反应;在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态须要和具体目标的状态保持一致。设计

  • 观察者模式的主要优势在于能够实现表示层和数据逻辑层的分离,并在观察目标和观察者之间创建一个抽象的耦合,支持广播通讯;其主要缺点在于若是一个观察目标对象有不少直接和间接的观察者的话,将全部的观察者都通知到会花费不少时间,并且若是在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能致使系统崩溃。

本文已经收录在系列文章Laravel核心代码学习里,欢迎访问阅读。

相关文章
相关标签/搜索