阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 责任链模式(Chain of Responsibility)的请求处理流程

责任链模式(Chain of Responsibility)的请求处理流程

作者:陈川 阅读数:60271人阅读 分类: JavaScript

责任链模式的基本概念

责任链模式是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。这种模式解耦了请求的发送者和接收者,使得多个对象都有机会处理请求。

在JavaScript中,责任链模式通常表现为一个对象包含对另一个对象的引用,形成一条链。当客户端发起请求时,请求会沿着这条链传递,直到有一个对象处理它为止。

责任链模式的结构

典型的责任链模式包含以下几个关键组成部分:

  1. Handler(抽象处理者):定义处理请求的接口,通常包含一个处理请求的方法和一个设置后继者的方法
  2. ConcreteHandler(具体处理者):实现抽象处理者的接口,处理它负责的请求,可以访问它的后继者
  3. Client(客户端):创建处理链,并向链上的具体处理者对象提交请求
// 抽象处理者
class Handler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler; // 方便链式调用
  }

  handle(request) {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }
}

// 具体处理者A
class ConcreteHandlerA extends Handler {
  handle(request) {
    if (request === 'A') {
      return `HandlerA处理了请求: ${request}`;
    }
    return super.handle(request);
  }
}

// 具体处理者B
class ConcreteHandlerB extends Handler {
  handle(request) {
    if (request === 'B') {
      return `HandlerB处理了请求: ${request}`;
    }
    return super.handle(request);
  }
}

// 使用示例
const handlerA = new ConcreteHandlerA();
const handlerB = new ConcreteHandlerB();

handlerA.setNext(handlerB);

console.log(handlerA.handle('A')); // HandlerA处理了请求: A
console.log(handlerA.handle('B')); // HandlerB处理了请求: B
console.log(handlerA.handle('C')); // null

责任链模式的实现方式

在JavaScript中,责任链模式有多种实现方式,下面介绍几种常见的实现方法:

1. 经典实现方式

如上面的示例所示,通过设置后继者形成链式结构。这是最接近传统面向对象语言的实现方式。

2. 使用数组实现责任链

class HandlerChain {
  constructor() {
    this.handlers = [];
  }

  addHandler(handler) {
    this.handlers.push(handler);
    return this; // 支持链式调用
  }

  handle(request) {
    for (const handler of this.handlers) {
      const result = handler.handle(request);
      if (result !== null) {
        return result;
      }
    }
    return null;
  }
}

// 具体处理者
class DiscountHandler {
  handle(amount) {
    if (amount >= 1000) {
      return amount * 0.9; // 9折
    }
    return null;
  }
}

class ShippingHandler {
  handle(amount) {
    if (amount < 500) {
      return amount + 50; // 加运费
    }
    return null;
  }
}

// 使用示例
const chain = new HandlerChain();
chain.addHandler(new DiscountHandler()).addHandler(new ShippingHandler());

console.log(chain.handle(800));  // 800 (无折扣,免运费)
console.log(chain.handle(1200)); // 1080 (9折)
console.log(chain.handle(300));  // 350 (加运费)

3. 使用函数实现责任链

JavaScript的函数是一等公民,可以利用这个特性实现更简洁的责任链:

function createHandlerChain(...handlers) {
  return function(request) {
    for (const handler of handlers) {
      const result = handler(request);
      if (result !== null) {
        return result;
      }
    }
    return null;
  };
}

// 处理函数
function managerHandler(request) {
  if (request.amount <= 1000) {
    return `经理批准了${request.amount}元的采购`;
  }
  return null;
}

function directorHandler(request) {
  if (request.amount <= 5000) {
    return `总监批准了${request.amount}元的采购`;
  }
  return null;
}

function ceoHandler(request) {
  if (request.amount <= 10000) {
    return `CEO批准了${request.amount}元的采购`;
  }
  return null;
}

// 创建责任链
const approvalChain = createHandlerChain(managerHandler, directorHandler, ceoHandler);

// 使用示例
console.log(approvalChain({ amount: 800 }));   // 经理批准了800元的采购
console.log(approvalChain({ amount: 3000 }));  // 总监批准了3000元的采购
console.log(approvalChain({ amount: 8000 }));  // CEO批准了8000元的采购
console.log(approvalChain({ amount: 20000 })); // null

责任链模式在前端开发中的应用场景

责任链模式在前端开发中有许多实际应用场景,下面列举几个典型例子:

1. 事件冒泡机制

DOM事件冒泡本身就是一种责任链模式的实现。事件从最具体的元素开始,逐级向上传播到较为不具体的节点。

document.getElementById('child').addEventListener('click', function(e) {
  console.log('Child clicked');
  // e.stopPropagation(); // 阻止继续向上传播
});

document.getElementById('parent').addEventListener('click', function() {
  console.log('Parent clicked');
});

document.body.addEventListener('click', function() {
  console.log('Body clicked');
});

2. 中间件机制

Express/Koa等框架的中间件机制就是责任链模式的典型应用:

const Koa = require('koa');
const app = new Koa();

// 中间件1
app.use(async (ctx, next) => {
  console.log('Middleware 1 - start');
  await next();
  console.log('Middleware 1 - end');
});

// 中间件2
app.use(async (ctx, next) => {
  console.log('Middleware 2 - start');
  await next();
  console.log('Middleware 2 - end');
});

// 路由处理
app.use(async ctx => {
  console.log('Route handler');
  ctx.body = 'Hello World';
});

app.listen(3000);

3. 表单验证

责任链模式非常适合处理复杂的表单验证逻辑:

class Validator {
  constructor() {
    this.nextValidator = null;
  }

  setNext(validator) {
    this.nextValidator = validator;
    return validator;
  }

  validate(input) {
    if (this.nextValidator) {
      return this.nextValidator.validate(input);
    }
    return { isValid: true, message: '' };
  }
}

class RequiredValidator extends Validator {
  validate(input) {
    if (!input.value) {
      return { isValid: false, message: `${input.name}是必填项` };
    }
    return super.validate(input);
  }
}

class EmailValidator extends Validator {
  validate(input) {
    if (input.type === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value)) {
      return { isValid: false, message: '请输入有效的邮箱地址' };
    }
    return super.validate(input);
  }
}

class LengthValidator extends Validator {
  constructor(min, max) {
    super();
    this.min = min;
    this.max = max;
  }

  validate(input) {
    if (input.value && (input.value.length < this.min || input.value.length > this.max)) {
      return { 
        isValid: false, 
        message: `${input.name}长度必须在${this.min}-${this.max}个字符之间`
      };
    }
    return super.validate(input);
  }
}

// 使用示例
const requiredValidator = new RequiredValidator();
const emailValidator = new EmailValidator();
const lengthValidator = new LengthValidator(6, 20);

requiredValidator.setNext(emailValidator).setNext(lengthValidator);

const formInput = {
  name: '密码',
  type: 'password',
  value: '123'
};

const result = requiredValidator.validate(formInput);
console.log(result); // { isValid: false, message: '密码长度必须在6-20个字符之间' }

责任链模式的变体与扩展

1. 异步责任链

在实际开发中,处理者可能需要执行异步操作。我们可以通过返回Promise来实现异步责任链:

class AsyncHandler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  async handle(request) {
    if (this.nextHandler) {
      return await this.nextHandler.handle(request);
    }
    return null;
  }
}

class AuthHandler extends AsyncHandler {
  async handle(request) {
    console.log('AuthHandler processing...');
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 1000));
    if (!request.token) {
      throw new Error('未授权');
    }
    return super.handle(request);
  }
}

class LoggingHandler extends AsyncHandler {
  async handle(request) {
    console.log('LoggingHandler processing...');
    await new Promise(resolve => setTimeout(resolve, 500));
    console.log(`请求日志: ${JSON.stringify(request)}`);
    return super.handle(request);
  }
}

class BusinessHandler extends AsyncHandler {
  async handle(request) {
    console.log('BusinessHandler processing...');
    await new Promise(resolve => setTimeout(resolve, 800));
    return `处理结果: ${request.data}`;
  }
}

// 使用示例
(async () => {
  const authHandler = new AuthHandler();
  const loggingHandler = new LoggingHandler();
  const businessHandler = new BusinessHandler();

  authHandler.setNext(loggingHandler).setNext(businessHandler);

  try {
    const result = await authHandler.handle({
      token: 'abc123',
      data: '重要业务数据'
    });
    console.log(result); // 处理结果: 重要业务数据
  } catch (error) {
    console.error('错误:', error.message);
  }
})();

2. 可中断的责任链

有时我们需要在某些条件下中断责任链的执行:

class InterruptibleHandler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  handle(request) {
    const result = this.process(request);
    if (result.shouldStop) {
      return result.value;
    }
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }

  process(request) {
    // 默认实现,子类可以覆盖
    return { shouldStop: false, value: null };
  }
}

class CacheHandler extends InterruptibleHandler {
  constructor(cache) {
    super();
    this.cache = cache || {};
  }

  process(request) {
    if (this.cache[request.key]) {
      return {
        shouldStop: true,
        value: `从缓存获取: ${this.cache[request.key]}`
      };
    }
    return { shouldStop: false, value: null };
  }
}

class DataHandler extends InterruptibleHandler {
  process(request) {
    // 模拟数据处理
    const result = `处理后的数据: ${request.key.toUpperCase()}`;
    return {
      shouldStop: true, // 处理完后中断链
      value: result
    };
  }
}

// 使用示例
const cache = { foo: '缓存值' };
const cacheHandler = new CacheHandler(cache);
const dataHandler = new DataHandler();

cacheHandler.setNext(dataHandler);

console.log(cacheHandler.handle({ key: 'foo' })); // 从缓存获取: 缓存值
console.log(cacheHandler.handle({ key: 'bar' })); // 处理后的数据: BAR

3. 多功能责任链

责任链中的处理者不仅可以处理请求,还可以修改请求或响应:

class TransformHandler {
  constructor() {
    this.nextHandler = null;
  }

  setNext(handler) {
    this.nextHandler = handler;
    return handler;
  }

  handle(request) {
    // 预处理请求
    const processedRequest = this.preProcess(request);
    
    // 传递给下一个处理者
    let response;
    if (this.nextHandler) {
      response = this.nextHandler.handle(processedRequest);
    } else {
      response = { status: 'default' };
    }
    
    // 后处理响应
    return this.postProcess(response);
  }

  preProcess(request) {
    // 默认不处理,子类可覆盖
    return request;
  }

  postProcess(response) {
    // 默认不处理,子类可覆盖
    return response;
  }
}

class RequestLogger extends TransformHandler {
  preProcess(request) {
    console.log('收到请求:', request);
    return {
      ...request,
      timestamp: Date.now()
    };
  }

  postProcess(response) {
    console.log('返回响应:', response);
    return response;
  }
}

class AuthTransformer extends TransformHandler {
  preProcess(request) {
    if (!request.token) {
      throw new Error('缺少token');
    }
    return {
      ...request,
      userId: this.extractUserId(request.token)
    };
  }

  extractUserId(token) {
    // 模拟从token中提取用户ID
    return token.split('-')[0];
  }
}

class BusinessLogic extends TransformHandler {
  handle(request) {
    // 不调用nextHandler,作为链的终点
    return {
      status: 'success',
      data: `处理用户${request.userId}的请求: ${request.action}`
    };
  }
}

// 使用示例
const logger = new RequestLogger();
const auth = new AuthTransformer();
const business = new BusinessLogic();

logger.setNext(auth).setNext(business);

try {
  const response = logger.handle({
    token: '12345-abcde',
    action: 'updateProfile'
  });
  console.log('最终响应:', response);
} catch (error) {
  console.error('处理失败:', error.message);
}

责任链模式的优缺点

优点

  1. 降低耦合度:请求发送者不需要知道哪个对象会处理它的请求,接收者也不需要知道请求的全貌
  2. 动态组合:可以动态地添加或修改处理链,增加新的处理类很方便
  3. 单一职责:每个处理类只需关注自己负责的部分,符合单一职责原则
  4. 灵活性:可以灵活地调整处理顺序或跳过某些处理步骤

缺点

  1. 性能考虑:请求可能遍历整个链才能被处理,在最坏情况下可能影响性能
  2. 调试困难:请求的传递是隐式的,调试时可能难以跟踪请求的处理过程
  3. 保证处理:不能保证请求一定会被处理,可能需要额外的逻辑来处理未被处理的请求
  4. 链的维护:如果链的构建不当,可能导致循环引用或处理顺序错误

责任链模式与其他模式的关系

与装饰器模式的关系

责任链模式和装饰器模式都基于递归组合的思想,但它们的目的是不同的:

  • 装饰器模式动态地给对象添加职责,所有装饰器都会执行
  • 责任链模式允许请求被一个或多个处理者处理,处理可能在链的任意环节停止

与命令模式的关系

责任链模式常与命令模式一起使用:

  • 命令模式将请求封装为对象
  • 责任链模式决定由哪个对象来处理这个命令对象

与组合模式的关系

组合模式的结构与责任链模式相似,但目的不同:

  • 组合模式用于表示部分-整体层次结构
  • 责任链模式用于处理请求的传递

实际项目中的最佳实践

1. 合理控制链的长度

过长的责任链会影响性能并增加调试难度。在实践中,建议:

  • 将链的长度控制在合理范围内(通常不超过10个处理者)
  • 对于复杂逻辑,可以考虑将部分处理合并
  • 使用组合模式将相关处理者组合成子链

2. 明确的处理结果约定

确保所有处理者对处理结果有一致的约定,例如:

  • 返回null或undefined表示未处理
  • 返回特定值表示已处理
  • 抛出异常表示处理失败

3. 提供调试支持

为了方便调试,可以:

  • 为处理链添加日志记录功能
  • 实现可视化工具展示请求在链中的流动
  • 提供性能监控,识别瓶颈处理者
class DebuggableHandler extends Handler {
  constructor(name) {
    super();
    this.name = name;
  }

  handle(request) {
    console.log(`[${this.name}] 处理请求:`, request);
    const start = performance.now();
    
    const result = super.handle(request);
    
    const duration = performance.now() - start;
    console.log(`[${this.name}] 处理完成,耗时: ${duration.toFixed(2)}ms`);
    
    return result;
  }
}

4. 考虑安全因素

在涉及安全相关的处理链中:

  • 确保关键处理者不能被跳过
  • 验证处理链的完整性
  • 考虑使用不可变请求对象防止中间修改

5. 与Promise链的结合

在现代JavaScript中,可以将责任链与Promise链结合使用:

function createPromiseChain(...handlers) {
  return function(input) {
    return handlers.reduce((promise, handler) => {
      return promise.then(result => {
        if (result.handled) {
          return result;
        }
        return handler(input);
      });
    }, Promise.resolve({ handled: false }));
  };
}

// 处理函数
function checkAuth(input) {
  return new Promise(resolve => {
    setTimeout(() => {
      if (!input.token) {
        resolve({ handled: true, error: '未授权' });
      } else {
        resolve({ handled: false });
      }
    }, 300);
  });
}

function validateInput(input) {
  return new Promise(resolve => {
    setTimeout(() => {
      if (!input.data) {
        resolve({ handled: true, error: '无效输入' });
      } else {
        resolve({ handled: false });
      }
    }, 200);
  });
}

function processData(input) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ 
        handled: true,
        result: `处理成功: ${input.data.toUpperCase()}`
      });
    }, 500);

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌