JS设计模式之职责链模式:优雅地处理请求流程

news2024/10/8 11:38:57

image.png

一. 前言

在前端开发中,我们经常会遇到需要按照一定的顺序处理一系列请求或操作的情况,如果将每一步处理都硬编码在一起,会导致代码臃肿,可维护性和可扩展性都会大大降低。而职责链模式恰好提供了一种优雅的解决方案。

无论你是希望优化代码结构、加强代码的灵活性,还是提高代码的可维护性和可扩展性,职责链模式都是一个非常有价值的设计模式。

接下来,跟随我一起探索 JavaScript 中的职责链模式!

二. 什么是职责链模式

1. 基础概念

职责链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许多个对象(处理者)依次处理同一个请求,直到其中的一个处理者能够处理该请求为止。在职责链模式中,请求沿着链条依次传递,每个处理者决定自己是否处理请求,如果处理者无法处理请求,则将请求传递给链条上的下一个处理者。

在 JavaScript 中,职责链模式通常由一系列对象(处理者)组成,它们共同形成一个处理请求的链条。每个处理者都包含一个处理请求的方法,并且拥有一个指向下一个处理者的引用。当一个请求被发起时,它将从链条的开头开始传递,直至有一个处理者能够完全处理请求为止。

职责链模式包括以下几个核心概念:

  1. 处理者(Handler):处理者是职责链模式中的关键角色,每个处理者都定义了处理请求的方法,并持有一个指向下一个处理者的引用。处理者通常包含一个处理请求的方法(例如handleRequest),在该方法中处理请求或者将请求传递给下一个处理者。

  2. 链条(Chain):处理者构成了一个链条,请求沿着链条从一个处理者传递到另一个处理者。链条的组织和顺序决定了请求的处理顺序。

  3. 请求(Request):请求是需要被处理的对象或信息。请求经过链条中的每个处理者,直到有一个处理者能够完全处理请求。

  4. 传递请求(Passing the Request):处理者在处理请求时,可以选择自己处理请求,也可以将请求传递给下一个处理者。这样,请求可以着链条传递并得到处理。

通过职责链模式,我们可以将请求的发送者和接收者解耦,灵活地构建处理请求的流程。这样可以增强代码的灵活性和可维护性,同时使得系统更易于扩展和修改。

2. UML 图

根据上面的几个核心概念,我们可以对职责链模式使用以下 UML 类图来描述:

image.png

在上面的类图中:

  • Handler 是抽象处理者,定义了一个处理请求的接口和一个指向下一个处理者的引用(successor)。

  • ConcreteHandlerAConcreteHandlerB 是具体处理者,实现了 Handler 中定义的处理请求的方法 handleRequest,并根据自身责任来处理请求。如果自己无法处理,就将请求传递给下一个处理者。

在职责链模式中,请求从发起经过一条链条上的处理者依次处理,直到找到合适的处理者为止。每个处理者都只关注自己的处理逻辑,可以灵活地添加、修改或删除处理者,使系统更加灵活和可扩展。

3. 作用

JavaScript 职责链模式的作用主要包括以下几个方面:

  1. 解耦责任处理者:将发送者和接收者解耦,使发送者无需知道具体的接收者是谁,以及如何处理请求。每个处理者只需关注自己的责任范围,大大降低了处理者之间的耦合度。

  2. 动态处理请求:职责链模式的链条结构可以动态地调整和修改,可以随时添加新的处理者或者移除现有的处理者,以适应不同的需求。

  3. 灵活性:每个处理者只需关注自己的责任,可以根据请求的不同部分进行不同的处理,使得系统更容易进行扩展和维护。

  4. 降低耦合度:将不同的处理逻辑分离开来,使得每个处理者只需关注自己的责任范围,降低模块之间的耦合度,提高系统的可维护性和可扩展性。

职责链模式的作用在于提供了一种灵活、可扩展的方式来处理复杂的请求处理流程,降低模块之间的耦合度,使得系统更容易维护和扩展。

三. 实现方式

在 JavaScript 中,实现职责链模式通常需要以下几个步骤:

  1. 定义处理者对象,处理者对象需要包含处理请求的方法,并且持有对下一个处理者的引用。

  2. 创建一个链条,将处理者按照一定的顺序连接起来。

  3. 发送请求,并且沿着链条传递请求,直到找到能够完全处理请求的处理者为止。

简单示例代码:

// 定义处理者对象
class Handler {
  constructor(name) {
    this.name = name;
    this.nextHandler = null;
  }

  setNextHandler(handler) {
    this.nextHandler = handler;
  }

  handleRequest(request) {
    if (this.canHandleRequest(request)) {
      console.log(`${this.name} 处理了请求:${request}`);
    } else if (this.nextHandler) {
      console.log(`${this.name} 不能处理请求,传递给下一个处理者`);
      this.nextHandler.handleRequest(request);
    } else {
      console.log("没有处理者可以处理该请求");
    }
  }

  canHandleRequest(request) {
    return false;
  }
}

// 创建具体的处理者对象
class ConcreteHandler1 extends Handler {
  canHandleRequest(request) {
    return request === "A";
  }
}

class ConcreteHandler2 extends Handler {
  canHandleRequest(request) {
    return request === "B";
  }
}

class ConcreteHandler3 extends Handler {
  canHandleRequest(request) {
    return request === "C";
  }
}

// 创建处理者链条
const handler1 = new ConcreteHandler1("Handler 1");
const handler2 = new ConcreteHandler2("Handler 2");
const handler3 = new ConcreteHandler3("Handler 3");

handler1.setNextHandler(handler2);
handler2.setNextHandler(handler3);

// 发送请求
handler1.handleRequest("A");
handler1.handleRequest("B");
handler1.handleRequest("C");
handler1.handleRequest("D");

执行结果如下图所示:

image.png

在上面的示例中,我们首先定义了一个抽象的处理者类Handler,具体的处理者对象ConcreteHandler1ConcreteHandler2ConcreteHandler3继承自抽象处理者类,并实现了具体的请求处理逻辑。然后创建了一个处理者链条,指定了处理者之间的顺序,并最后发送了多种不同的请求,观察处理者是如何根据自己的能力来处理请求或者将请求传递给下一个处理者的。

四. 应用场景

在 JavaScript 中,我总结了职责链模式适用的以下几种场景:

  1. 表单验证:在前端开发中,表单验证是一个常见的需求。可以使用职责链模式来实现多个验证规则的处理流程,每个验证规则作为一个处理者,根据规则是否匹配来决定是否处理表单验证请求。

  2. 请求处理:在网络请求处理中,可能需要经过多个处理步骤,每个处理步骤都有自己的处理逻辑,这时可以使用职责链模式来实现请求处理流程。

  3. 权限校验:在权限校验中,可能存在不同级别的权限验证逻辑,使用职责链模式可以实现逐级权限验证,直到找到对应权限处理者为止。

除此之外,可以在其他方面也有类似的使用该模式应用的场景,例如:事件处理、异常处理等等,类似的还有很多。但总体来说,职责链模式适用于处理流程含有多个处理步骤,并且这些处理步骤可以灵活组合和扩展的场景。

下面我以表单验证为例,使用职责链模式来逐步验证表单输入是否符合要求。

// 定义处理者对象
class Validator {
  constructor(name, validatorFn) {
    this.name = name;
    this.validatorFn = validatorFn;
    this.nextValidator = null;
  }

  setNextValidator(validator) {
    this.nextValidator = validator;
  }

  validate(value) {
    if (this.validatorFn(value)) {
      if (this.nextValidator) {
        return this.nextValidator.validate(value);
      } else {
        return true;
      }
    } else {
      return false;
    }
  }
}

// 具体的验证函数
const isNotEmpty = (value) => {
  return value.trim() !== "";
};

const isEmail = (value) => {
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailPattern.test(value);
};

const isNumber = (value) => {
  return !isNaN(value) && isFinite(value);
};

// 创建验证链条
const validator1 = new Validator("NotEmpty", isNotEmpty);
const validator2 = new Validator("IsEmail", isEmail);
const validator3 = new Validator("IsNumber", isNumber);

validator1.setNextValidator(validator2);
validator2.setNextValidator(validator3);

// 模拟表单数据
const formData = {
  email: "example@example.com",
  age: "25",
  address: "",
};

// 进行表单验证
for (let key in formData) {
  const isValid = validator1.validate(formData[key]);
  console.log(`${key} validation: ${isValid}`);
}

在上述代码中,首先定义了三个具体的验证函数isNotEmptyisEmailisNumber,分别用于验证字段是否为空、是否为邮箱格式和是否为数字。然后创建了三个验证器对象,分别对应这三种验证函数,并且按照验证顺序连接起来形成了一个验证链条。最后,模拟了一个表单数据对象formData,对每个字段进行逐个验证,并输出验证结果。

image.png

这样,使用职责链模式来实现表单验证,可以在每个验证器中专注于自己的验证逻辑,保持代码的清晰度,同时也能方便地对验证规则进行扩展和定制。

五. 优缺点

通过以上的了解,总结一下在 JavaScript 中使用职责链模式的一些优点和缺点:

优点:

  1. 解耦责任:职责链模式将请求的发送者和接收者解耦,每个处理者只需关注自己的具体责任,使得系统更灵活、更易于扩展和维护。

  2. 简化对象:相比于将所有的处理逻辑集中在一个对象中,职责链模式将每个处理逻辑封装成一个独立的对象,避免了单个对象过于臃肿和复杂。

  3. 灵活性:可以根据需要动态地调整处理链条,可以随时添加、修改、删除处理者,灵活应对各种变化的需求。

  4. 封装性:每个处理者都只关心自己的处理逻辑,对其他处理者内部逻辑无需了解,增强了各处理者之间的独立性。

缺点:

  1. 性能损耗:因为需要遍历整个链条来寻找正确的处理者,所以在链条较长的情况下可能会导致性能损耗,特别是在处理大量数据时。

  2. 链条滞留:如果链条设计不合理或者处理者设置不当,可能导致请求在链条中滞留无法得到正确处理,影响系统的性能和稳定性。

  3. 调试困难:由于请求在链条中传递,可能需要跟踪整个链条才能定位问题,增加了调试的难度。

  4. 过度使用:如果你在追求过度使用职责链模式,可能会使系统变得复杂而难以理解。

因此,职责链模式在适当的场景下能够有效地简化代码结构、提高系统灵活性,但也需要谨慎使用,避免出现性能问题和复杂度提高。

六. 结语

在 JavaScript 中,职责链模式是一种非常有用的设计模式,它可以帮助我们更好地组织代码、简化逻辑、提高系统的灵活性和可扩展性。通过在不同处理者之间建立连接,并在需要时动态地调整处理链条,我们可以更好地应对复杂的业务逻辑和需求变化。

在编写和应用职责链模式时,我们需要注意合理地设计链条结构,避免性能损耗和链条滞留等问题。同时,也要注意避免过度使用职责链模式,保持代码的简洁和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2196262.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python进阶--正则表达式

目录 1. 基础匹配 2. 元字符匹配 1. 基础匹配 正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则&#xff…

HyperBDR云容灾“轻装上阵”,露脸魔都写字楼!

回顾今年828,万博智云作为铂金合作伙伴在仪式上惊艳亮相,同时还参与了华为云“云上大咖团”的直播分享,公司明星产品HyperBDR云容灾成功上线了828企业应用一站购平台。就在近日,后续来啦! 一、HyperBDR云容灾楼宇广告…

特斯拉的TTPoE协议到底是啥?比TCP/IP快n倍?真的假的…

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部 上午好,我的网工朋友 最近,在备受瞩目的Hot Chips大会上,特斯拉不仅展示了其最新的DOJO超级计算机项目&#…

叉车AI行人防撞预警系统,提升作业安全与效率

如今,发展飞快的工业蓝海中,叉车作为物流仓储与制造业不可或缺的搬运工具,其安全性一直是行业内外关注的焦点。随着人工智能技术的日益成熟,工业(车辆)叉车AI行人防撞预警系统解决方案应运而生,…

零基础入门AI大模型应用开发,你需要一个系统的学习路径!

前言 随着人工智能技术的迅猛发展,特别是在大型语言模型(LLMs)领域的突破,AI大模型已经成为当今科技领域的热门话题。不论是对于希望转型进入AI行业的职场人士,还是对未来充满憧憬的学生,掌握AI大模型的应…

JS 介绍/书写位置/输入输出语法

目录 1. JS 介绍 1.1 JS 是什么 1.2 JS 的作用 1.3 JS 的组成 2. JS 书写位置 2.1 内部 JS 2.2 外部 JS 2.3 内联 JS 3. JS 注释和结束符 4. JS 输入输出语法 4.1 输入语法 4.2 输入语句 4.3 执行顺序 5. 字面量 1. JS 介绍 1.1 JS 是什么 1.2 JS 的作用 1.3 JS …

上海AI Lab视频生成大模型书生.筑梦环境搭建推理测试

引子 最近视频生成大模型层出不穷,上海AI Lab推出新一代视频生成大模型 “书生・筑梦 2.0”(Vchitect 2.0)。根据官方介绍,书生・筑梦 2.0 是集文生视频、图生视频、插帧超分、训练系统一体化的视频生成大模型。OK,那就让我们开始吧。 一、模…

国产长芯微LPQ9008菊花链接口的18单元电池监控器完全P2P替代ADBMS1818

描述 LPQ9008是一款多单元电池堆监控器,可测量多达18个串联连接的电池单元,总测量误差小于3.0 mV。LPQ9008具有0 V至5 V的电池测量范围,适合大多数电池化学应用。可在290 μs内测量所有18个电池单元,并选择较低的数据采集速率以便…

24.10.7(线段树合并,分裂,扫描线,字符串哈希)

星期一: 昨晚熬夜场的div2总算是不负有心人,到C都比较简单,出C后我也没有run的想法,一直在看D,最后5min的时间ac,小小上了波分 贴cf round976 div2 D cf传送门 题意&a…

从学习Java到学习AI大模型,我为什么选择的后者???

我为什么从Java转到AI大模型 在编程的海洋里,Java一直是我信赖的“小船”,载着我航行在代码的世界中。然而,随着行业的不断发展和变化,我开始感受到了一丝的迷茫和不安。我开始担心,随着技术的不断更新,Ja…

银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证

银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证 💖The Begin💖点点关注,收藏不迷路💖 使用银河麒麟V10 SP1安装应用时,若频繁遇到安全授权认证提示,可按以下步骤设置: 打开…

操作系统 | 学习笔记 | 王道 | 4.3 文件系统

4.3 文件系统 4.3.1 文件系统结构 文件系统(File system)提供高效和便捷的磁盘访问,以便允许存储、定位、提取数据。 用一个例子来辅助记忆文件系统的层次结构: 假设某用户请求删除文件"D:/工作目录/学生信息.xIsx"的最后100条记录。 用户需…

Linux——磁盘分区、挂载

Linux 分区 原理介绍 原理图如下 当我们在/home目录下新建一个文件a.txt时,该文件实际上是存放在硬盘B的分区1中的,这就是图里说的,当进入某个目录,可以进入到该目录下挂载的分区里的意思 硬盘说明 应用实例:挂载一个…

镁稀土中间合金的耐腐蚀性

镁稀土中间合金,也称作镁稀土合金,是一种重要的合金材料,由镁、稀土元素(如镧、铈、镨、钕、钷、钐、铕、钆、铽、镝、钬、铒、镱、镥等)以及其他可能的金属元素(如铝、锶、锆、钙等)组成。以下是对镁稀土中间合金的详细介绍: 一、…

六、索引的数据结构

文章目录 1. 为什么使用索引2. 索引及其优缺点2.1 索引概述2.2 优点2.3 缺点3. InnoDB中索引的推演3.1 索引之前的查找3.1.1 在一个页中的查找3.1.2 在很多页中查找3.2 设计索引3.2.1 一个简单的索引设计方案3.2.2 InnoDB中的索引方案3.3 常见索引概念3.3.1 聚簇索引3.3.2 二级…

CPU Study - Recovery when Prediction Fails

参考来源:《超标量处理器设计》—— 姚永斌 分支预测失败时,这条分支指令之后的所有指令都处在了错误的路径上(mis-prediction)。 这些指令都会被抹除掉 ,从而造成很多bubble,降低处理器性能,称…

数学概念算法-打印100以内的素/质数

素数:只能被1和自己整除的数 暴力破解 埃氏筛选 找到第一个数字,如果它是素数,则把它的倍数全部划掉 比如数字2是素数,那么 4,6,8,10,12。这些数字肯定不是素数,所以不用再考虑,直接划掉即可 第二步&#…

ROS理论与实践学习笔记——3 ROS运行管理之ROS话题名称设置

名称重映射是为名称起别名,为名称添加前缀,该实现比节点重名更复杂些,不单是使用命名空间作为前缀、还可以使用节点名称最为前缀。两种策略的实现途径有多种: (1)rosrun 命令 (2)launch 文件 …

camody卡魔迪-准备新一年双十一推出iPhone快充充电宝

随着双十一购物节的临近,各大品牌纷纷推出创新产品以吸引消费者的目光。近日,知名科技品牌Camody卡魔迪宣布将在今年双十一期间推出一款全新的iPhone快充充电宝。这款充电宝不仅具备快速充电功能,还兼具时尚设计和便携性,预计将成…

ArcGIS中分区统计栅格值前需要进行投影吗(在投影坐标系下进行吗),为什么?

最近,我接到了一个分区统计栅格数值前需要进行投影,或者说是必须需要在投影坐标系下进行吗的咨询。 答案是不需要刻意去变。 但是他又说他把地理坐标系下分区统计结果与投影坐标系下的分区统计结果分别做了一遍,并进行了对比,两个…