Skip to content

Menu
Menu

观察者模式

Posted on 2023年4月8日2024年12月4日 by zhezimi

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象(通常被称为主题或者被观察者)的状态发生变化时,所有依赖它的对象(观察者)都会得到通知并自动更新。观察者模式主要用于实现分布式系统,事件驱动系统和数据绑定等场景。

角色

观察者模式的类结构图:

观察者模式包含以下几个主要角色

1:Subject(目标主题):被观察的目标主题的接口抽象,维护观察者对象列表,并定义注册方法register()(订阅)与通知方法notify()(发布)。

2:ConcreteSubject(主题实现):被观察的目标主题的具体实现类。

3: Observer(观察者):观察者的接口抽象,定义响应方法update()。

4: ConcreteObserver(观察者实现):观察者的具体实现类,可以有任意多个子类实现。实现了响应方法update(),收到通知后进行自己独特的处理。

工作流程

1:观察者对象将自己注册到被观察者对象的观察者列表中,表明它对被观察者的状态感兴趣

2:当被观察者的状态发生改变时,它会遍历观察者列表,并逐个调用观察者的更新方法,通知它们状态已经改变

3:当观察者接收到通知后,会指向相应的更新操作,以保持与被观察者状态的同步

优点跟缺点

优点:

1:支持松耦合:被观察者跟观察者的依赖关系是抽象的,它们可以独立地进行修改和扩展,而不会相互影响

2:动态添加和删除观察者:观察者可以随时注册和注销,使得系统可以灵活地响应需求变化。

缺点:

1:通知所有观察者可能导致效率降低,特别是在观察者数量较多的情况下

2:如果存在循环依赖,可能导致观察者和被观察者间的死循环,因此,在实现观察者模式时,需要注意避免循环依赖的问题。

实现方式

1:推模型

被观察者主动向观察者推送所需的数据。这种方式的优点是观察者可以直接使用数据,无需自己获取。但缺点是,如果观察者并不需要所有数据,这样会导致数据传输的浪费。

2:拉模型

被观察者仅通知观察者发生了变化,由观察者自己决定是否需要获取数据。这种方式的优点是避免了不必要的数据传输,但缺点是观察者需要额外的操作来获取数据

实际工作应用场景

1:事件驱动系统

在事件驱动的系统中,当某个事件发生时,需要通知其他组件执行相应的操作。例如,在web应用中,用户注册成功后,需要发送欢迎邮件并记录注册日志。使用观察者模式,可以将邮件发送和日志记录作为观察者,将用户注册作为被观察者。当用户注册成功时,通知观察者执行相应的操作。

2:实时数据更新

实时数据更新场景,例如股票行情。使用观察者模式,可以使得当股票价格发生变化时,所有订阅了该股票的客户端(观察者)得到通知并更新数据。在这种情况下,股票行情是被观察者,客户端是观察者。

3:模型-视图-控制器(MVC)架构

MVC架构是一种常见的软件架构模式,其中模型(Model)负责管理数据和业务逻辑,视图(View)负责展示数据,控制器(Controller)负责接收用户输入并更新模型。在这种架构中,可以使用观察者模式使得当模型发生变化时,通知视图更新。模型是被观察者,视图是观察者。

4:配置管理

在许多应用程序中,需要对配置文件进行监控,以便在配置发生变化时,实时更新程序的运行状态。在这种情况下,可以使用观察者模式,将配置文件作为被观察者,将依赖配置文件的组件作为观察者。当配置文件发生变化时,通知观察者更新其状态。

代码实现

1:定义观察者接口(Observer)

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

2:定义被观察者接口(Subject)

interface Subject
{
    public function registerObserver(Observer $observer);

    public function removeObserver(Observer $observer);

    public function notifyObservers();
}

3:实现一个具体的被观察者(WeatherData):

class WechatData implements Subject
{
    private $observers;

    private $temperature;

    private $humidity;

    private $pressure;

    public function __construct()
    {
        $this->observers = [];
    }

    public function registerObserver(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function removeObserver(Observer $observer)
    {
        $index = array_search($observer, $this->observers);
        if ($index !== false) {
            unset($this->observers[$index]);
        }
    }

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

    public function setMeasurements($temperature, $humidity, $pressure)
    {
        $this->temperature = $temperature;
        $this->humidity = $humidity;
        $this->pressure = $pressure;
        $this->notifyObservers();
    }

    public function getTemperature()
    {
        return $this->temperature;
    }

    public function getHumidity()
    {
        return $this->humidity;
    }

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

}

4:定义一个具体的观察者

class CurrentConditionsDisplay implements Observer
{
    private $temperature;
    private $humidity;

    public function __construct(Subject $wechatData)
    {
        $wechatData->registerObserver($this);
    }

    public function update($subject)
    {
        if ($subject instanceof WechatData) {
            $this->temperature = $subject->getTemperature();
            $this->humidity = $subject->getHumidity();
            $this->display();
        }
    }

    public function display()
    {
        echo "Current conditions:" . $this->temperature . "°C and " . $this->humidity . "% humidity" . PHP_EOL;
    }
}

5:开始使用

$wechatData = new WechatData();
$currentConditionsDisplay = new currentConditionsDisplay($wechatData);
$wechatData->setMeasurements(25, 65, 1013);
$wechatData->setMeasurements(22, 70, 1012);

这个例子中,WeatherData 是一个具体的被观察者,它实现了 Subject 接口,当气象数据发生变化时,它会通知所有注册的观察者。CurrentConditionsDisplay 是一个具体的观察者,它实现了 Observer 接口,当被通知气象数据发生变化时,它会更新自己的状态并显示当前的温度和湿度。

PHP框架的观察者模式

在很多PHP常用的框架中,都使用到了观察者模式,那么我们以Yii框架为例:

Yii 框架中的观察者模式主要体现在事件处理机制上。当某个类的特定事件发生时,它可以触发其他类(事件处理器)的方法。这种机制类似于观察者模式中的被观察者和观察者之间的关系。

在 Yii 中,可以使用如下方法为类绑定事件处理器:

$component->on($eventName, $handler);

其中 $component 是触发事件的类(被观察者),$eventName 是事件名称,$handler 是事件处理器(观察者)。

当 $component 的特定事件发生时,Yii 会自动调用与之关联的所有事件处理器。

更多信息请查看https://www.yiiframework.com/doc/guide/2.0/zh-cn/concept-events

下面我们来写一个简单的Yii事件处理示例:

1:定义一个自定义事件

class CustomEvent extends Event
{
    public $message;
}

2:创建一个类(被观察者)

class MyClass extends Component
{
    const EVENT_MY_CUSTOM_EVENT = ‘myCustomEvent’;

    public function triggerCustomEvent()
    {
        $event = new CustomEvent();
        $event->message = "hello Observer";
        $this->trigger(self::EVENT_MY_CUSTOM_EVENT, $event);
    }
}

3:创建一个事件处理器(观察者)

class MyEventHandler
{
    public function handleCustomEvent(CustomEvent $event)
    {
        echo "Event trigger with message:" . $event->message;
    }
}

4:使用

$myObject = new MyClass();
$myEventHandler = new MyEventHandler();

$myObject->on(MyClass::EVENT_MY_CUSTOM_EVENT, [$myEventHandler, ’handleCustomEvent’]);

$myObject->triggerCustomEvent();

在这个示例中,我们创建了一个 MyClass 类(被观察者)和一个 MyEventHandler 事件处理器(观察者)。MyClass 类定义了一个名为 EVENT_MY_CUSTOM_EVENT 的自定义事件。然后,我们将 MyEventHandler 的 handleCustomEvent 方法绑定到 MyClass 的自定义事件上。最后,当 MyClass 的自定义事件被触发时,MyEventHandler 中的 handleCustomEvent 方法会自动执行。

题外话:相信大家在用到yii框架的时候,肯定会很熟悉beforeSave 和 afterSave 方法,那么这两个方法是否是观察者模式呢?因为它们是观察到有save操作而触发的动作。从本质上来说,这两个方法可以算做钩子方法,因为它们是直接在基类(ActiveRecord)定义的,而不是完全独立的观察者类。

触发模式

观察者模式可以看作是一种触发模式。当被观察者(如A操作)发生变化时,它会通知所有依赖于它的观察者(如B操作和C操作),这样就实现了A操作触发B操作和C操作的效果。

观察者模式的优势在于它实现了被观察者和观察者之间的松耦合。这意味着你可以在不修改被观察者的情况下,添加或删除观察者。因此,观察者模式不仅实现了触发关系,还保持了系统的灵活性和可扩展性。

参考书籍

《秒懂设计模式》

《Head First 设计模式》

相关文章

  • 装饰者模式

发表评论 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

近期文章

  • 排查网络故障常用命令
  • PHP-FPM异常问题
  • RabbitMQ 1:介绍
  • 观察者模式
  • 装饰者模式

近期评论

没有评论可显示。

分类

  • cdn
  • css
  • docker
  • git
  • http
  • javascript
  • linux
  • mysql
  • nginx
  • php
  • RabbitMQ
  • 代码规范
  • 性能
  • 正则表达式
  • 网络协议
  • 设计模式
© 2025 | Powered by Minimalist Blog WordPress Theme