19. 职责链模式(Chain of Responsibility)

问题:请求需要经过多个处理者,但不想让发送者知道谁会处理。

核心:处理者串成链,每个处理者决定自己处理还是传给下一个。

type RequestContext = {
  token?: string;
  role?: string;
  user?: string;
};

class Handler {
  #next?: Handler;
  setNext(h: Handler): Handler { this.#next = h; return h; }
  handle(request: RequestContext): string | null {
    if (this.#next) return this.#next.handle(request);
    return null;
  }
}

class AuthHandler extends Handler {
  handle(request: RequestContext): string | null {
    if (!request.token) return '未认证';
    return super.handle(request);
  }
}

class RoleHandler extends Handler {
  handle(request: RequestContext): string | null {
    if (request.role !== 'admin') return '权限不足';
    return super.handle(request);
  }
}

class LogHandler extends Handler {
  handle(request: RequestContext): string | null {
    console.log(`管理员 ${request.user} 操作已记录`);
    return super.handle(request);
  }
}

const auth = new AuthHandler();
auth.setNext(new RoleHandler()).setNext(new LogHandler());

const result = auth.handle({ token: 'abc', role: 'admin', user: '小明' });
// 管理员 小明 操作已记录
console.log(result); // null(全部通过)
package chain

import "fmt"

type Handler interface {
  SetNext(Handler) Handler
  Handle(request map[string]any) string
}

type BaseHandler struct{ next Handler }

func (h *BaseHandler) SetNext(next Handler) Handler {
  h.next = next
  return next
}

func (h *BaseHandler) Handle(request map[string]any) string {
  if h.next != nil {
    return h.next.Handle(request)
  }
  return ""
}

type AuthHandler struct{ BaseHandler }

func (h *AuthHandler) Handle(req map[string]any) string {
  if _, ok := req["token"]; !ok {
    return "未认证"
  }
  return h.BaseHandler.Handle(req)
}

type RoleHandler struct{ BaseHandler }

func (h *RoleHandler) Handle(req map[string]any) string {
  if role, _ := req["role"].(string); role != "admin" {
    return "权限不足"
  }
  return h.BaseHandler.Handle(req)
}

type LogHandler struct{ BaseHandler }

func (h *LogHandler) Handle(req map[string]any) string {
  user, _ := req["user"].(string)
  fmt.Printf("管理员 %s 操作已记录\n", user)
  return h.BaseHandler.Handle(req)
}

实战场景

  • Web 中间件:请求依次经过日志、鉴权、限流、参数校验、业务处理、错误处理。
  • 表单校验链:必填、格式、长度、业务唯一性、权限检查按顺序执行,失败就提前返回。
  • 客服工单升级:机器人、普通客服、高级客服、专家团队逐级处理,当前层处理不了就传给下一层。
  • 日志处理:debug、info、warn、error 由不同 handler 判断是否处理或继续传递。
  • 权限判断:先检查登录,再检查角色,再检查资源归属,再检查特殊策略。
  • 支付风控:设备风险、账号风险、金额风险、黑名单、人工审核一层层判断。
  • 编译器处理:词法分析、语法分析、类型检查、优化、代码生成形成处理链。
  • 数据清洗:去空值、格式化、标准化、去重、补默认值按步骤传递数据。
  • API 网关:跨域、鉴权、路由、熔断、负载均衡、响应改写都可以作为链上节点。
  • 插件流水线:构建工具的 loader、transform、hook 往往就是一条可扩展处理链。

Express/Koa 的中间件机制就是这个模式。