观察者模式的总结
21

什么是观察者模式

观察者模式用于实现对对象进行观察:一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。

举个例子说明:
假设一个这样的情景,当公司有一个新员工入职了,入职的当天,HR需要为他办理入职手续,网管需要给他配好电脑和办公用品,部门主管需要带他熟悉部门。传统的编程方式,就是在员工入职这个事件发生的代码之后直接加入处理逻辑,当后续我们需要增加处理逻辑时(比如员工入职后增加培训),代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要改变事件主题的代码。运用观察者模式,将员工的入职作为事件,其他的处理逻辑都做为观察者的操作,那么,当以后需要再增加更多的逻辑时,新增逻辑代码就会很方便。具体代码实现如下。

代码实现

首先定义一个观察者接口,所有的观察者都实现这个接口(为什么要定义成接口呢?因为每一个观察者的具体行为需要具体去实现,用接口定义一个统一的方法,具体的实现交给观察者去实现)

interface observer
{
    public function update();
}

再定义一个事件生成器的抽象类,用来使继承它的事件都具有通知观察者的能力。

abstract class EventGenerator
{
    private $observers = [];

    //定义一个添加观察者的方法
    public function addOberver(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    //定义一个通知观察者的方法
    public function notify()
    {
        foreach($this->observers as $observer)
        {
            $observer->update();
        }
    }

}

然后再来定义事件类

class event extends EventGenerator
{
    //定义一个触发观察者的方法
    public funtion trigger()
    {
        echo "Event <br/>";

        //开始通知观察者
        $this->notify();
    }   
}

开始使用

$event = new event();
$event->trigger();

这个时候,当我们要在事件发生的时候增加别的操作,只需要新增观察者就可以了

新增一个观察者

class Observer1 implements Observer
{
    public function update()
    {
        echo "操作1<br/>";    
    }

}

然后使用的时候就是

$event = new event();
// 增加观察者
$event->addObserver(new Observer1);
$event->trigger();

如果需要在事件发生后再增加操作,只需再新增相应的观察者即可。

应用观察者模式的好处

观察者模式解除了主体和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。降低对象之间的耦合度以达到解耦的目的,符合"开闭原则"的要求。

利用 SPL 实现观察者模式

PHP 通过内置的 SPL 扩展提供了对观察者模式的原生支持,其中的观察者由 3 个元素组成 : SplObserver 接口、 SplSubject 接口和 SplObjectStorage 工具类。下面是利用 SPL 实现观察者模式的代码。SPL 的地址见这里


class MyObserver1 implements SplObserver {
    public function update(SplSubject $subject) {
        echo __CLASS__ . ' - ' . $subject->getName();
    }
}

class MyObserver2 implements SplObserver {
    public function update(SplSubject $subject) {
        echo __CLASS__ . ' - ' . $subject->getName();
    }
}

class MySubject implements SplSubject {
    private $observers;
    private $name;

    public function __construct($name) {
        $this->observers = new SplObjectStorage();
        $this->name = $name;
    }

    public function attach(SplObserver $observer) {
        $this->observers->attach($observer);
    }

    public function detach(SplObserver $observer) {
        $this->observers->detach($observer);
    }

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

    public function getName() {
        return $this->name;
    }
}

$observer1 = new MyObserver1();
$observer2 = new MyObserver2();

$subject = new MySubject("test");

$subject->attach($observer1);
$subject->attach($observer2);
$subject->notify();

/* 
输出:

MyObserver1 - test
MyObserver2 - test
*/

$subject->detach($observer2);
$subject->notify();

/* 
输出:

MyObserver1 - test
*/

临渊羡鱼,退而结网。

本帖由系统于 8个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 7
736713830

每次都能看明白.但是到了用的时候根本想不起来....时间一长还有可能忘记了...这怎么破.

10个月前
Jeffrey00

@736713830 光看明白是不够的,一方面需要去实际应用,另一方面,你也可以试着自己总结起来,这样会有更加深入的理解

10个月前
736713830

@Jeffrey00 嗯 感谢.已经动手理解了一遍.

10个月前
736713830

@Jeffrey00 当才华撑不起野心时,就静下来学习。 我喜欢这句话.

10个月前
godruoyi

我最近也在瞅设计模式,共勉!! :+1:

10个月前
Jeffrey00

@736713830 :smile:

10个月前
Jeffrey00

@godruoyi 共勉~

10个月前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!