JavaScript 职责链模式实战:从业务重构到 AOP 实现

Mia
Mia
发布时间:2026-10-15| 3人浏览过
原创

职责链模式通过将请求发送者与多个接收者解耦,让多个对象都有机会处理请求。本文将结合 JS 电商订单场景,演示如何使用类封装和 AOP 技术构建灵活的责任链,并解决异步传递问题。

1. 核心概念与现实映射

职责链模式(Chain of Responsibility)旨在减少请求发送者与接收者之间的耦合度。多个处理对象被连接成一条链,请求沿链传递,直到有一个对象成功处理它。这种模式避免了“硬编码”特定的接收者。

最直观的现实例子是早高峰乘坐公交投币:乘客将硬币递给前方的人,若对方不是售票员,则继续向前传递,直到遇到售票员为止。在这个过程中,发送者(乘客)无需知道链中具体哪个人是接收者(售票员),只需将请求发给第一个节点即可。这有效弱化了发送者与一组接收者之间的强联系。

2. 典型痛点:复杂的条件分支

在实际开发中,当业务逻辑涉及多层判断时,往往会产生难以维护的巨大型函数。以手机电商网站的订单处理为例:

  • orderType:1 表示 500 元定金用户,2 表示 200 元定金用户,3 为普通购买用户。
  • pay:布尔值,标识是否已支付定金(未支付则降级为普通购买)。
  • stock:当前库存数量。

若使用传统的 if-else 结构,代码会陷入深层嵌套,且新增规则时需频繁修改原有逻辑,违背开放-封闭原则:

const order = function (orderType, pay, stock) {
	if (orderType === 1) {
		if (pay === true) {
			console.log('500 元定金预购, 得到 100 优惠券');
		} else {
			// ... 降级逻辑及库存判断
		}
	} 
	// 此处省略大量冗长的嵌套代码
};

这种写法不仅阅读困难,更导致后续维护成为噩梦。我们需要引入职责链模式来拆分这些逻辑。

3. 基础重构:将请求传递给下一个节点

首先,我们将不同的订单类型拆分为独立的小函数。核心思路是:如果当前函数无法处理该请求,则直接调用下一个函数的逻辑。

  1. 500 元订单节点:若满足定金且已支付,则执行优惠逻辑;否则,调用 order200。
  2. 200 元订单节点:若满足条件,执行对应优惠;否则,调用 orderNormal。
  3. 普通购买节点:检查库存,若有货则输出提示,无货则报错。
const order500 = function (orderType, pay, stock) {
	if (orderType === 1 && pay === true) {
		console.log('500 元定金预购, 得到 100 优惠券');
	} else {
		order200(orderType, pay, stock); // 将请求传递给下一节点
	}
};

const order200 = function (orderType, pay, stock) {
	if (orderType === 2 && pay === true) {
		console.log('200 元定金预购, 得到 50 优惠券');
	} else {
		orderNormal(orderType, pay, stock); // 将请求传递给普通订单
	}
};

const orderNormal = function (orderType, pay, stock) {
	if (stock > 0) {
		console.log('普通购买, 无优惠券');
	} else {
		console.log('手机库存不足');
	}
};

虽然代码可读性提升,但请求的传递路径(如 order500 内部调用 order200)仍然硬编码在函数体内。若要增加或删除节点,仍需深入修改每个函数的源码,耦合度并未根本降低。

4. 进阶方案:灵活的可拆分节点

为彻底解耦,我们采用面向对象的方式定义一个 Chain 构造函数,将原始函数包装为职责链节点。约定俗成的做法是:如果当前节点不能处理请求,返回特定字符串 'nextSuccessor'。

实现细节如下:

  • this.fn:存储传入的业务逻辑函数。
  • this.successor:指向链中的下一个节点。
  • setNextSuccessor(successor):设置链的下家。
  • passRequest(...args):向节点发起请求。若返回 nextSuccessor,则自动委托给下家处理。
const Chain = function (fn) {
	this.fn = fn;
	this.successor = null;
};

Chain.prototype.setNextSuccessor = function (successor) {
	return this.successor = successor;
};

Chain.prototype.passRequest = function () {
	const ret = this.fn.apply(this, arguments);
	if (ret === 'nextSuccessor') {
		return this.successor && this.successor.passRequest.apply(this.successor, arguments);
	}
	return ret;
};

组装链条时,先分别包装函数,再指定顺序:

const chainOrder500 = new Chain(order500);
const chainOrder200 = new Chain(order200);
const chainOrderNormal = new Chain(orderNormal);

chainOrder500.setNextSuccessor(chainOrder200);
chainOrder200.setNextSuccessor(chainOrderNormal);

// 测试入口
chainOrder500.passRequest(1, true, 500);

此时,新增一个“300 元定金”节点变得异常简单:只需定义新函数,包装为 new Chain(),并在链中调整连接关系即可,无需触碰其他节点源码。这种配置化的方式极大提升了系统的扩展性。

5. 高级挑战:异步职责链

在 Web 开发中,常需发起 Ajax 请求获取后端状态。由于异步特性,无法同步返回 'nextSuccessor' 字符串来决定是否继续传递。为此,需在 Chain 原型上增加 next 方法,允许手动触发向下一节点的传递:

Chain.prototype.next = function () {
	return this.successor && this.successor.passRequest.apply(this.successor, arguments);
};

使用示例中,节点内可使用定时器或回调模拟异步流程,完成后调用 this.next():

const fn1 = new Chain(function () {
	console.log(1);
	return 'nextSuccessor';
});
const fn2 = new Chain(function () {
	console.log(2);
	setTimeout(() => {
		this.next(); // 手动控制何时传递给下一个节点
	}, 1000);
});
const fn3 = new Chain(function () {
	console.log(3);
});

fn1.setNextSuccessor(fn2).setNextSuccessor(fn3);
fn1.passRequest();

通过组合命令模式封装 Ajax 请求,可以轻松构建出一个异步的请求队列库。

6. 巧妙变体:使用 AOP 实现

除了类封装,利用 JavaScript 高阶函数的特性,也可以通过修饰模式(AOP思想)简化链条实现。通过在 Function.prototype 上添加 after 方法:

Function.prototype.after = function (fn) {
	const self = this;
	return function () {
		const ret = self.apply(this, arguments);
		if (ret === 'nextSuccessor') {
			return fn.apply(this, arguments);
		}
		return ret;
	}
};

// 使用方法
const order = order500yuan.after(order200yuan).after(orderNormal);

AOP 方式的代码更为简洁紧凑,但由于闭包层层嵌套,作用域链过长可能会对长链条的性能产生一定影响。

7. 优缺点评估与应用场景

改进后的职责链结构:

  • 优点:
    1. 解耦:切断请求方与具体接收方的依赖,符合开闭原则。
    2. 灵活性:运行时可自由增删改查节点及其顺序。
    3. 自定义起点:可以不从链头开始传递,直接交给中间任意节点,提高响应效率。
  • 缺点:
    1. 请求可能未被处理:若链尾没有保底逻辑,请求可能悬空。建议在最后增加一个默认的 error 处理节点。
    2. 性能损耗:过长的链条可能导致不必要的递归调用开销。

在 DOM 事件冒泡、Ajax 请求拦截器等场景中,职责链模式都能发挥重要作用。学会合理运用此模式,能显著提升代码的整洁度与可维护性。

以上就是 JavaScript 设计模式中职责链模式的详细内容,更多关于 JS 设计模式原理与优化的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。