责任链模式和 Pipeline 管道模式有什么区别?
简化版
责任链强调请求沿处理器传递,某个节点可以处理、放行、短路或转交;Pipeline 更强调数据经过一系列阶段转换,每个阶段通常都会执行并输出给下游。责任链关注“谁负责处理以及是否继续”,Pipeline 关注“数据如何逐步加工”。
详细版
两者都可能表现为 A -> B -> C,所以很容易混淆。责任链更常见于过滤、鉴权、审批、异常处理、风控拦截,节点可能决定不再往下走。Pipeline 更常见于编译器、数据清洗、图像处理、ETL、构建流程,每个阶段把输入转换成输出。
如果请求是订单提交,登录校验 -> 参数校验 -> 风控 -> 库存校验 更像责任链,因为任何一步都可能拒绝请求。如果是日志处理,原始日志 -> 解析 -> 清洗 -> 聚合 -> 入库 更像 Pipeline,因为每一步都在加工数据。
面试中可以从 4 个维度区分:执行目标、是否短路、数据形态是否变化、节点之间是否承担同类职责。
完整版教学
一、为什么两者容易混淆
责任链和 Pipeline 都经常画成一条线:A -> B -> C -> D。如果只看结构,确实很像;但设计模式不能只看结构,要看意图。责任链的意图是让多个处理者有机会处理同一请求,发送者不直接指定具体处理者;Pipeline 的意图是把数据处理拆成多个阶段,让数据逐步变换。
责任链:
Request -> Handler A(pass/stop) -> Handler B(pass/stop) -> Handler C
Pipeline:
Input -> Stage A(transform) -> Stage B(transform) -> Stage C(output)
一个简单判断方法:如果你最关心“哪个节点可以拦截或负责处理”,多半是责任链;如果你最关心“数据从一种形态变成另一种形态”,多半是 Pipeline。
记忆钩子:责任链问“要不要继续给下一个处理者”,Pipeline 问“下一个阶段拿到什么数据”。
二、责任链的核心是处理机会和短路
责任链中每个节点通常面对同一个上下文对象,决定自己是否处理、是否继续。比如请求过滤链里,鉴权失败就短路,限流失败也短路,只有都通过才进入业务处理。这种链路的关键是决策。
HTTP Request
-> AuthFilter: PASS
-> RateLimitFilter: STOP(too many requests)
-x-> Controller
如果 1000 个请求里有 50 个未登录、30 个被限流,那么这 80 个请求不会到达 Controller。责任链通过短路减少后续处理,也把拒绝原因定位在具体节点。
三、Pipeline 的核心是分阶段加工数据
Pipeline 中每个阶段通常都会执行,并把输出交给下游。比如日志 ETL:解析阶段把字符串变成结构化对象,清洗阶段去掉无效字段,聚合阶段按用户或接口汇总,入库阶段写入存储。它的重点是数据形态变化。
raw line
-> ParseStage: LogEvent
-> CleanStage: CleanLogEvent
-> AggregateStage: MetricPoint
-> SinkStage: write to storage
| 阶段 | 输入 | 输出 |
|---|---|---|
| 解析 | 原始字符串 | 结构化日志 |
| 清洗 | 结构化日志 | 干净日志 |
| 聚合 | 干净日志 | 指标点 |
| 入库 | 指标点 | 写入结果 |
Pipeline 也可能有错误跳过或停止,但那通常是异常控制或数据质量策略,不是它的主要意图。
四、两者的返回值和上下文设计不同
责任链常用共享上下文和统一决策结果,比如 PASS/STOP/ERROR。Pipeline 更常见的是阶段输入输出类型,可以是同一类型,也可以每步不同类型。比如 Java Stream 更像 Pipeline:filter -> map -> sorted -> collect,每一步处理数据流。
// 责任链风格
ChainResult handle(RequestContext context, Chain chain);
// Pipeline 风格
Output process(Input input);
| 维度 | 责任链 | Pipeline |
|---|---|---|
| 关注点 | 是否处理、是否继续 | 数据如何变换 |
| 数据形态 | 多为共享上下文 | 常逐步转换 |
| 节点结果 | PASS/STOP/ERROR | 新数据或处理结果 |
| 高频场景 | 鉴权、过滤、审批 | ETL、编译、构建 |
五、责任链节点通常职责同类,Pipeline 阶段可以差异更大
责任链上的节点往往都在做“处理同一个请求”的某类职责,比如一组校验器、一组过滤器、一组审批人。Pipeline 的阶段可能差异更大,解析、清洗、聚合、写入是完全不同的加工步骤,但它们通过输入输出串起来。
责任链校验:
LoginCheck -> ParamCheck -> RiskCheck -> StockCheck
Pipeline 编译:
LexicalAnalysis -> SyntaxAnalysis -> SemanticAnalysis -> CodeGenerate
当然,这不是绝对界限。现实系统中两者会混合,比如 Pipeline 某个阶段内部用责任链做校验,责任链某个处理器内部用 Pipeline 做数据加工。面试时要讲“主要意图”,不要被类名困住。
六、从短路语义看边界最清楚
短路是区分两者的一个强信号。责任链中短路通常是正常业务语义,比如鉴权失败、审批拒绝、规则命中一票否决。Pipeline 中中断通常更像异常或过滤,比如坏数据丢弃、解析失败进死信队列。
责任链短路:
RiskHandler -> STOP(RISK_REJECT) -> 返回拒绝
Pipeline 中断:
ParseStage -> ERROR(BAD_FORMAT) -> 丢弃或进入错误流
如果一个流程里“被某个节点拒绝”是核心业务含义,它更像责任链。如果“经过每个阶段加工”才是核心含义,它更像 Pipeline。
七、面试回答可以用场景快速落地
回答区别题时,最好同时给出两个场景。比如 Web 请求过滤器更像责任链,日志 ETL 更像 Pipeline。再补一句“结构相似,但意图不同”,就能避免只背概念。
Web 安全过滤:
Request -> CORS -> Auth -> RateLimit -> Controller
任何节点可短路,责任链。
日志处理:
RawLog -> Parse -> Clean -> Aggregate -> Sink
每步转换数据,Pipeline。
还可以补充:Servlet FilterChain 既有责任链味道,也有管道味道,因为请求和响应会穿过多个 Filter。但面试中通常把它作为责任链例子,是因为每个 Filter 可以决定是否继续调用 chain.doFilter。
八、常见误区与追问
- 误区:只要是 A 到 B 到 C 就是责任链。 链式结构不等于责任链,要看是否有处理机会传递和短路语义。
- 误区:Pipeline 不能中断。 Pipeline 可以因错误中断,但中断通常不是它的核心业务意图。
- 误区:责任链不能改变上下文。 责任链可以修改上下文,但主要目标不是把输入逐步转换成不同输出类型。
- 追问:Servlet FilterChain 是责任链还是 Pipeline? 更常作为责任链,因为 Filter 可以选择是否继续;从请求响应穿过多个阶段看,也带有管道特征。
- 追问:两者能组合吗? 可以,Pipeline 某阶段内部可用责任链做校验,责任链某个节点内部可用 Pipeline 做数据加工。
- 追问:如何选择? 如果核心是拦截、放行、转交,选责任链;如果核心是分阶段加工和转换,选 Pipeline。
九、加强记忆
区分责任链和 Pipeline,抓住“决策”和“变换”:责任链的节点做决策,决定处理、放行、短路;Pipeline 的阶段做变换,把输入加工成下游需要的输出。两者外形都像链,但一个围绕请求处理权,一个围绕数据处理过程。