首页 后端开发 php教程 了解观察者模式

了解观察者模式

Feb 28, 2025 am 09:14 AM

Understanding the Observer Pattern

核心要点

  • 观察者模式是一种行为型设计模式,它在对象之间建立一对多的关系,当一个对象改变其状态时,所有依赖对象都会自动收到通知并更新。
  • 该模式包含一个主题(或发布者)和观察者(或订阅者)。主题通知观察者任何状态变化,观察者可以相应地采取行动。此模式促进松散耦合,使系统更灵活,更容易修改或扩展。
  • 观察者类提供一个update()方法,主题调用该方法以通知其状态变化。主题类定义主要方法:attach()detach()setState()notify(),用于管理观察者并通知他们状态变化。
  • 观察者模式适用于一个对象的更改需要更改其他对象的情况,尤其是在要更改的对象数量未知时。它可以用于在相关对象之间保持一致性,而无需紧密耦合它们。例如,它用于将门户的身份验证机制与论坛软件链接,允许用户使用单个登录名登录两者。

我最近被要求将第三方论坛软件集成到现有的 Web 门户中。问题是,这两个应用程序都有各自独立的身份验证机制。从用户的角度来看,理想情况下,用户无需单独登录论坛即可登录门户。在这种情况下,我不希望不必要地修改论坛代码,因为这会造成维护噩梦——我必须合并供应商的任何后续更新和错误修复,并小心避免覆盖我自己的修改。这就是观察者模式对我来说非常方便的地方。在本文中,我将向您展示如何实现观察者模式。您将学习模式中各种类如何相互关联作为主题和观察者,主题如何通知观察者其状态的变化,以及如何在您自己的代码中识别适合使用观察者模式的场景。

观察者模式

观察者模式(也称为发布-订阅模式)是一种行为型设计模式,它定义对象之间的一对多关系,以便当一个对象改变其状态时,所有依赖对象都会自动收到通知并更新。在我的例子中,我使用此模式将门户的身份验证机制与论坛软件链接。登录门户的行为也会自动触发用户登录论坛。与其他对自身状态感兴趣的对象具有一对多关系的对象称为主题发布者。其依赖对象称为观察者订阅者。每当主题的状态发生变化时,观察者都会收到通知,并可以相应地采取行动。主题可以拥有任意数量的依赖观察者,它会向这些观察者发出通知,并且任意数量的观察者都可以订阅主题以接收此类通知。

观察者类

观察者类提供一个update()方法,主题将调用该方法以通知其状态变化。在此示例中,我已将update()方法定义为具体方法。如果您愿意,您可以在此处将该方法定义为抽象方法,然后在观察者的子类中为其提供具体实现。

<?php
abstract class Observer
{
    public function __construct($subject = null) {
        if (is_object($subject) && $subject instanceof Subject) {
            $subject->attach($this);
        }
    }

    public function update($subject) {
        // 查找具有状态名称的观察者方法
        if (method_exists($this, $subject->getState())) {
            call_user_func_array(array($this, $subject->getState()), array($subject));
        }
    }
}
登录后复制
登录后复制

__construct()方法接受可观察主题的实例,并将自身附加到主题——我稍后会讲到。update()方法检索主题的当前状态,并使用它来调用与状态名称相同的子类化观察者方法。因此,在我的特定情况下,我需要将门户现有的 Auth 类设为可观察主题,并创建一个具体的观察者子类来连接到论坛的身份验证代码。我的子类还需要使用主题的状态来实现方法。

主题类

主题类也是一个抽象类,它定义了四个主要方法:attach()detach()setState()notify()。为方便起见,我还在此处添加了getState()getObservers()方法。

<?php
abstract class Subject
{
    protected $observers;
    protected $state;

    public function __construct() {
        $this->observers = array();
        $this->state = null;
    }

    public function attach(Observer $observer) {
        $i = array_search($observer, $this->observers);
        if ($i === false) {
            $this->observers[] = $observer;
        }
    }

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

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

    public function setState($state) {
        $this->state = $state;
        $this->notify();
    }

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


    public function getObservers() {
        return $this->observers;
    }
}
登录后复制

attach()方法将观察者订阅到主题,以便可以向其传达任何状态更改。detach()方法将观察者从主题中取消订阅,以便它不再观察主题的状态更改。setState()方法设置主题的当前状态并调用notify()来更新观察者,即向每个观察者发布通知。notify()方法依次通过迭代内部列表并调用每个成员的update()方法来更新每个已订阅的对象。getState()getObservers()方法只是返回当前主题的状态和观察者列表的辅助函数。

仔细观察……整合在一起

现在有了观察者和主题的抽象基类,我能够将论坛软件集成到现有的 Web 门户中。我需要将门户的 Auth 类设为可观察主题,并在用户登录或注销门户时设置其可观察状态。

<?php
class Auth extends Subject
{
    function login() {
        // 执行登录身份验证的现有代码
        // ...

        // 向任何观察者发出信号,表明用户已登录
        $this->setState("login");
    }

    function logout() {
        // 执行用户注销时执行某些操作的现有代码
        // 例如销毁会话等...

        // 向任何观察者发出信号,表明用户已注销
        $this->setState("logout");
    }
}
登录后复制

我扩展了 Subject 类,以便 Auth 可观察,然后在 login()logout() 方法中添加了对 setState() 的调用。为了对观察者进行子类化,我创建了一个 Auth_ForumHook 类,该类负责调用论坛的 API 登录和注销函数。

<?php
class Auth_ForumHook extends Observer
{
    function login($subject) {
        // 调用论坛的 API 函数以登录用户
        // ...
    }

    function logout($subject) {
        // 调用论坛的 API 函数以注销用户
        // ...
    }
}
登录后复制

在代码库的其他地方,在实例化门户的 Auth 类的地方,我将 Auth_ForumHook 实例附加到它,以便观察者会收到 Auth 中任何状态更改的通知。

<?php
abstract class Observer
{
    public function __construct($subject = null) {
        if (is_object($subject) && $subject instanceof Subject) {
            $subject->attach($this);
        }
    }

    public function update($subject) {
        // 查找具有状态名称的观察者方法
        if (method_exists($this, $subject->getState())) {
            call_user_func_array(array($this, $subject->getState()), array($subject));
        }
    }
}
登录后复制
登录后复制

除了准备抽象的 Observer 和 Subject 类之外,这就是我的额外编码需求的全部内容。Auth 的 login()logout() 方法触发的任何状态更改都会通知 Auth_ForumHook 观察者,并自动登录或注销论坛中的用户。要添加新的观察者,例如,登录跟踪器以记录用户何时登录或注销门户,只需提供一个具体的 Observer 类并将其附加到 Auth 主题即可,而无需进一步修改现有的 Auth 对象的 login()logout() 方法。

总结

如果您有多个依赖于另一个对象的对象,并且需要在该对象的状态发生变化时执行操作,或者一个对象需要通知其他对象而不知道它们是谁或有多少个,那么观察者模式是一个合适的适用设计模式。在本文中,我向您展示了基本的主题-观察者模式,并提供了具体的示例,说明如何使用这种行为模式轻松扩展现有类的功能,而无需紧密耦合任何新的需求。此模式使您能够在相关和依赖对象之间实现更高水平的一致性,而不会牺牲代码的可重用性。

图片来自 JPF / Shutterstock

(后续的FAQs部分,由于篇幅过长,已省略。核心内容已在上面重新组织和润色。)

以上是了解观察者模式的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在PHP API中说明JSON Web令牌(JWT)及其用例。 在PHP API中说明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息,主要用于身份验证和信息交换。1.JWT由Header、Payload和Signature三部分组成。2.JWT的工作原理包括生成JWT、验证JWT和解析Payload三个步骤。3.在PHP中使用JWT进行身份验证时,可以生成和验证JWT,并在高级用法中包含用户角色和权限信息。4.常见错误包括签名验证失败、令牌过期和Payload过大,调试技巧包括使用调试工具和日志记录。5.性能优化和最佳实践包括使用合适的签名算法、合理设置有效期、

PHP 8.1中的枚举(枚举)是什么? PHP 8.1中的枚举(枚举)是什么? Apr 03, 2025 am 12:05 AM

PHP8.1中的枚举功能通过定义命名常量增强了代码的清晰度和类型安全性。1)枚举可以是整数、字符串或对象,提高了代码可读性和类型安全性。2)枚举基于类,支持面向对象特性,如遍历和反射。3)枚举可用于比较和赋值,确保类型安全。4)枚举支持添加方法,实现复杂逻辑。5)严格类型检查和错误处理可避免常见错误。6)枚举减少魔法值,提升可维护性,但需注意性能优化。

会话如何劫持工作,如何在PHP中减轻它? 会话如何劫持工作,如何在PHP中减轻它? Apr 06, 2025 am 12:02 AM

会话劫持可以通过以下步骤实现:1.获取会话ID,2.使用会话ID,3.保持会话活跃。在PHP中防范会话劫持的方法包括:1.使用session_regenerate_id()函数重新生成会话ID,2.通过数据库存储会话数据,3.确保所有会话数据通过HTTPS传输。

描述扎实的原则及其如何应用于PHP的开发。 描述扎实的原则及其如何应用于PHP的开发。 Apr 03, 2025 am 12:04 AM

SOLID原则在PHP开发中的应用包括:1.单一职责原则(SRP):每个类只负责一个功能。2.开闭原则(OCP):通过扩展而非修改实现变化。3.里氏替换原则(LSP):子类可替换基类而不影响程序正确性。4.接口隔离原则(ISP):使用细粒度接口避免依赖不使用的方法。5.依赖倒置原则(DIP):高低层次模块都依赖于抽象,通过依赖注入实现。

解释PHP中的晚期静态绑定(静态::)。 解释PHP中的晚期静态绑定(静态::)。 Apr 03, 2025 am 12:04 AM

静态绑定(static::)在PHP中实现晚期静态绑定(LSB),允许在静态上下文中引用调用类而非定义类。1)解析过程在运行时进行,2)在继承关系中向上查找调用类,3)可能带来性能开销。

什么是REST API设计原理? 什么是REST API设计原理? Apr 04, 2025 am 12:01 AM

RESTAPI设计原则包括资源定义、URI设计、HTTP方法使用、状态码使用、版本控制和HATEOAS。1.资源应使用名词表示并保持层次结构。2.HTTP方法应符合其语义,如GET用于获取资源。3.状态码应正确使用,如404表示资源不存在。4.版本控制可通过URI或头部实现。5.HATEOAS通过响应中的链接引导客户端操作。

您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? 您如何在PHP中有效处理异常(尝试,捕捉,最后,投掷)? Apr 05, 2025 am 12:03 AM

在PHP中,异常处理通过try,catch,finally,和throw关键字实现。1)try块包围可能抛出异常的代码;2)catch块处理异常;3)finally块确保代码始终执行;4)throw用于手动抛出异常。这些机制帮助提升代码的健壮性和可维护性。

PHP中的匿名类是什么?您何时可以使用它们? PHP中的匿名类是什么?您何时可以使用它们? Apr 04, 2025 am 12:02 AM

匿名类在PHP中的主要作用是创建一次性使用的对象。1.匿名类允许在代码中直接定义没有名字的类,适用于临时需求。2.它们可以继承类或实现接口,增加灵活性。3.使用时需注意性能和代码可读性,避免重复定义相同的匿名类。

See all articles