一、责任链模式(Chain of Responsibility)定义
责任链模式是一种行为型设计模式 ,其核心思想是:将请求的发送者和接收者解耦,把多个处理对象连成一条链,请求沿着这条链传递,直到有一个对象处理该请求为止。
每个处理对象(处理器)都包含对下一个处理器的引用,当请求到达时,处理器先判断自己是否能处理该请求:
- 若能处理,则直接处理,不再传递;
- 若不能处理,则将请求转发给链中的下一个处理器。
官方定义:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
二、核心角色
(1)抽象处理者(Handler):定义处理请求的接口,包含抽象处理方法和一个指向后续处理器的引用。
(2)具体处理者(ConcreteHandler):实现抽象处理者的接口,判断是否能处理当前请求,若能则处理,否则转发给下一个处理器。
(3)客户端(Client):创建责任链,并将请求发送到链的第一个处理器。
三、应用场景
责任链模式适用于请求需要多个对象依次处理,且不确定哪个对象最终处理的场景,典型案例:
(1)请求审批流程:如请假审批(小组长→部门经理→总监→CEO),不同请假时长由不同层级审批;
(2)异常处理链:多层级的异常捕获与处理,上层处理器处理不了的异常交给下层;
(3)日志框架:不同级别日志(Debug→Info→Warn→Error)由不同的日志处理器处理;
(4)过滤器 / 拦截器:如 Web 框架中的请求拦截器(权限校验→参数校验→日志记录);
(5)事件分发:GUI 系统中的事件传递(如按钮点击事件沿组件树传递)。
四、优缺点
1、优点
(1)解耦请求发送者与接收者:发送者无需知道请求由谁处理,接收者无需知道请求的完整链路;
(2)灵活性高:可动态增删 / 调整处理器的顺序,符合 "开闭原则";
(3)单一职责:每个处理器只负责自己的处理逻辑,职责清晰;
(4)简化对象间交互:对象仅需持有下一个处理器的引用,无需维护所有处理器。
2、缺点
(1)性能风险:请求可能遍历整个链却无人处理,导致性能损耗;
(2)调试难度大:链的层级过多时,定位请求处理节点复杂;
(3)链的顺序依赖:若处理器顺序配置错误,可能导致请求处理异常;
(4)可能存在循环引用:若链的配置错误,可能出现请求无限循环传递。
五、C# 代码实例(请假审批场景)
1、场景说明
模拟公司请假审批流程:
- 请假时长≤1 天:小组长审批;
- 1 天 < 时长≤3 天:部门经理审批;
- 3 天 < 时长≤7 天:总监审批;
- 时长 > 7 天:CEO 审批;
- 超出范围则拒绝。
2、代码实现
csharp
using System;
// 1. 抽象处理者(审批者)
public abstract class Approver
{
// 下一个审批者
protected Approver NextApprover;
// 审批者名称
public string Name { get; }
public Approver(string name)
{
Name = name;
}
// 设置下一个审批者
public void SetNextApprover(Approver nextApprover)
{
NextApprover = nextApprover;
}
// 抽象审批方法
public abstract void ApproveLeave(LeaveRequest request);
}
// 2. 具体处理者:小组长
public class GroupLeader : Approver
{
public GroupLeader(string name) : base(name) { }
public override void ApproveLeave(LeaveRequest request)
{
if (request.Days <= 1)
{
Console.WriteLine($"[{Name}] 审批通过:{request.EmployeeName} 请假 {request.Days} 天");
}
else if (NextApprover != null)
{
Console.WriteLine($"[{Name}] 无权限审批,转发给下一级");
NextApprover.ApproveLeave(request);
}
else
{
Console.WriteLine($"[{Name}] 审批拒绝:无更高层级审批者");
}
}
}
// 具体处理者:部门经理
public class DepartmentManager : Approver
{
public DepartmentManager(string name) : base(name) { }
public override void ApproveLeave(LeaveRequest request)
{
if (request.Days > 1 && request.Days <= 3)
{
Console.WriteLine($"[{Name}] 审批通过:{request.EmployeeName} 请假 {request.Days} 天");
}
else if (NextApprover != null)
{
Console.WriteLine($"[{Name}] 无权限审批,转发给下一级");
NextApprover.ApproveLeave(request);
}
else
{
Console.WriteLine($"[{Name}] 审批拒绝:无更高层级审批者");
}
}
}
// 具体处理者:总监
public class Director : Approver
{
public Director(string name) : base(name) { }
public override void ApproveLeave(LeaveRequest request)
{
if (request.Days > 3 && request.Days <= 7)
{
Console.WriteLine($"[{Name}] 审批通过:{request.EmployeeName} 请假 {request.Days} 天");
}
else if (NextApprover != null)
{
Console.WriteLine($"[{Name}] 无权限审批,转发给下一级");
NextApprover.ApproveLeave(request);
}
else
{
Console.WriteLine($"[{Name}] 审批拒绝:无更高层级审批者");
}
}
}
// 具体处理者:CEO
public class CEO : Approver
{
public CEO(string name) : base(name) { }
public override void ApproveLeave(LeaveRequest request)
{
if (request.Days > 7 && request.Days <= 30)
{
Console.WriteLine($"[{Name}] 审批通过:{request.EmployeeName} 请假 {request.Days} 天");
}
else
{
Console.WriteLine($"[{Name}] 审批拒绝:{request.EmployeeName} 请假 {request.Days} 天(超出最大请假天数)");
}
}
}
// 请假请求实体
public class LeaveRequest
{
// 员工姓名
public string EmployeeName { get; set; }
// 请假天数
public int Days { get; set; }
}
// 3. 客户端调用
class Program
{
static void Main(string[] args)
{
// 构建审批链:小组长 → 部门经理 → 总监 → CEO
Approver groupLeader = new GroupLeader("小组长-张三");
Approver deptManager = new DepartmentManager("部门经理-李四");
Approver director = new Director("总监-王五");
Approver ceo = new CEO("CEO-赵六");
// 设置链的顺序
groupLeader.SetNextApprover(deptManager);
deptManager.SetNextApprover(director);
director.SetNextApprover(ceo);
// 测试不同请假天数的审批流程
Console.WriteLine("=== 测试1:请假1天 ===");
groupLeader.ApproveLeave(new LeaveRequest { EmployeeName = "小明", Days = 1 });
Console.WriteLine("\n=== 测试2:请假2天 ===");
groupLeader.ApproveLeave(new LeaveRequest { EmployeeName = "小红", Days = 2 });
Console.WriteLine("\n=== 测试3:请假5天 ===");
groupLeader.ApproveLeave(new LeaveRequest { EmployeeName = "小刚", Days = 5 });
Console.WriteLine("\n=== 测试4:请假10天 ===");
groupLeader.ApproveLeave(new LeaveRequest { EmployeeName = "小李", Days = 10 });
Console.WriteLine("\n=== 测试5:请假31天(超出范围) ===");
groupLeader.ApproveLeave(new LeaveRequest { EmployeeName = "小王", Days = 31 });
}
}
3、输出结果
csharp
=== 测试1:请假1天 ===
[小组长-张三] 审批通过:小明 请假 1 天
=== 测试2:请假2天 ===
[小组长-张三] 无权限审批,转发给下一级
[部门经理-李四] 审批通过:小红 请假 2 天
=== 测试3:请假5天 ===
[小组长-张三] 无权限审批,转发给下一级
[部门经理-李四] 无权限审批,转发给下一级
[总监-王五] 审批通过:小刚 请假 5 天
=== 测试4:请假10天 ===
[小组长-张三] 无权限审批,转发给下一级
[部门经理-李四] 无权限审批,转发给下一级
[总监-王五] 无权限审批,转发给下一级
[CEO-赵六] 审批通过:小李 请假 10 天
=== 测试5:请假31天(超出范围) ===
[小组长-张三] 无权限审批,转发给下一级
[部门经理-李四] 无权限审批,转发给下一级
[总监-王五] 无权限审批,转发给下一级
[CEO-赵六] 审批拒绝:小王 请假 31 天(超出最大请假天数)
六、总结
**1、核心价值:**责任链模式的核心是 "解耦",让请求的发送者和接收者完全隔离,符合 "迪米特法则";
**2、适用场景:**请求需要多节点处理、处理节点动态变化、无需明确知道处理者的场景;
3、使用建议:
- 控制链的长度,避免过长导致性能问题;
- 确保链的末端有默认处理逻辑(如拒绝请求),防止请求 "丢失";
- 可通过配置文件动态构建链,提升灵活性;
4、与其他模式的区别:
- 与装饰器模式:责任链是 "处理或转发",装饰器是 "增强功能并传递";
- 与状态模式:责任链的处理逻辑由处理器决定,状态模式的处理逻辑由对象状态决定。
责任链模式在实际开发中应用广泛(如ASP.NET Core 的中间件、过滤器),合理使用可大幅提升代码的扩展性和可维护性,但需注意避免过度设计导致链层级冗余。
