JavaScript 职责链模式实战:从业务重构到 AOP 实现
职责链模式通过将请求发送者与多个接收者解耦,让多个对象都有机会处理请求。本文将结合 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. 基础重构:将请求传递给下一个节点
首先,我们将不同的订单类型拆分为独立的小函数。核心思路是:如果当前函数无法处理该请求,则直接调用下一个函数的逻辑。
- 500 元订单节点:若满足定金且已支付,则执行优惠逻辑;否则,调用
order200。 - 200 元订单节点:若满足条件,执行对应优惠;否则,调用
orderNormal。 - 普通购买节点:检查库存,若有货则输出提示,无货则报错。
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. 优缺点评估与应用场景
改进后的职责链结构:
- 优点:
- 解耦:切断请求方与具体接收方的依赖,符合开闭原则。
- 灵活性:运行时可自由增删改查节点及其顺序。
- 自定义起点:可以不从链头开始传递,直接交给中间任意节点,提高响应效率。
- 缺点:
- 请求可能未被处理:若链尾没有保底逻辑,请求可能悬空。建议在最后增加一个默认的
error处理节点。 - 性能损耗:过长的链条可能导致不必要的递归调用开销。
- 请求可能未被处理:若链尾没有保底逻辑,请求可能悬空。建议在最后增加一个默认的
在 DOM 事件冒泡、Ajax 请求拦截器等场景中,职责链模式都能发挥重要作用。学会合理运用此模式,能显著提升代码的整洁度与可维护性。
以上就是 JavaScript 设计模式中职责链模式的详细内容,更多关于 JS 设计模式原理与优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









