欢迎光临
我们一直在努力

设计模式:责任链模式

设计模式:责任链模式

你是否写过这样的代码:一个请求进来,先判断 A 能不能处理,不能就判断 B,B 也不行就判断 C,C 还不行就抛异常。一层套一层的 if-else,写着写着自己都绕晕了。更麻烦的是,每次新增一个处理逻辑,你都得回去改这个 if-else 链。改着改着,这个函数就膨胀成几十行的条件判断,可读性和维护性都在急剧下降。

那么有没有一种方式,能让每个处理逻辑自己决定"这事我管不管",管就处理,不管就自动传给下一个人,用于优化这种全部 if-else 的方式?有的,兄弟,有的,这就是责任链模式。

目录

  • 责任链模式的定义与核心思想
  • 从 if-else 到责任链:为什么需要这种模式
  • 责任链模式的整体结构
  • 责任链模式的完整实现
  • 责任链模式与 if-else 的对比分析
  • 责任链模式的典型应用场景
  • 小结

责任链模式的定义与核心思想

责任链模式(Chain of Responsibility)是一种行为型设计模式。它的核心思想用一句话概括:把多个处理者串成一条链,请求沿着链传递,谁有能力处理谁就接手。

举个栗子,你在学校请病假。你把假条交给辅导员,辅导员只能批 1 天以内的假,你的申请是 3 天,他批不了,就把假条转给系主任。系主任能批 3 天以内的,3 天在他权限内,他签字审批,流程结束。

你从头到尾只做了一件事:提交假条。至于这张假条最终由谁审批、经过了几个环节,你并不需要关心。这就是责任链模式的核心价值:请求的发送者不需要知道谁会处理它。

从 if-else 到责任链:为什么需要这种模式

回到开头那个 if-else 的问题。假设你在写一个学生请假审批系统:

public String approveLeave(int days) {
if (days <= 1) {
return "辅导员审批通过";
} else if (days <= 3) {
return "系主任审批通过";
} else if (days <= 7) {
return "院长审批通过";
} else {
return "需要教务处处长审批";
}
}

这段代码逻辑可行,但有几个明显的问题:

1.扩展性差。 新增一个"教务处处长能批 14 天以内"的层级,你就得回去改这个方法,加一个 else if,把逻辑写进去。改一次没问题,改十次之后再看到这个方法可能就变得眼花缭乱,容易出错了。

2.违反开闭原则。 每次业务变更都要修改已有代码,而不是新增代码。这在团队协作中意味着你可能和别人产生代码冲突。

3.职责不清晰。 所有审批逻辑堆在一个方法里,每个处理者的边界是通过 if-else 的顺序隐式表达的,不是显式定义的。

责任链模式把每个处理者独立出来,各自管各自的逻辑,通过一条链串起来。新增处理者只需要往链上加一环,不需要动已有代码。

责任链模式的整体结构

先用一张图看清楚结构:

请求


处理者 A ──(处理不了)──→ 处理者 B ──(处理不了)──→ 处理者 C
│ │ │
▼ ▼ ▼
处理成功 处理成功 处理成功
(链终止) (链终止) (链终止)

请求从链头进入,依次经过每个处理者。每个处理者有两个选择:我能处理,就处理并结束;我处理不了,就传给下一个。如果整条链走完都没人处理,那就做兜底处理。

核心组件只有两个:

组件职责
抽象处理者(Handler) 定义处理接口,持有下一个处理者的引用
具体处理者(ConcreteHandler) 实现具体的处理逻辑,决定是处理还是传递

责任链模式的完整实现

定义抽象处理者

每个处理者都需要知道"我的下一个人是谁",并且提供一个处理请求的方法。这是所有处理者的共同行为,因此可以抽成一个抽象类:

public abstract class Handler {
// 持有下一个处理者的引用
private Handler next;

// 设置下一个处理者,返回 handler 方便链式调用
public Handler setNext(Handler handler) {
this.next = handler;
return handler;
}

// 处理请求的模板方法
public String handle(int days) {
if (next != null) {
return next.handle(days);
}
return "无法处理当前请假申请";
}
}

setNext 返回 handler 是一个小技巧,让链的组装可以写成 a.setNext(b).setNext(c) 这种链式风格,比一行一行 setNext 更简洁。

实现具体处理者

每个处理者只关心一件事:这个请假申请我能不能批。

// 辅导员:能批 1 天以内的
public class Counselor extends Handler {
@Override
public String handle(int days) {
if (days <= 1) {
return "辅导员审批通过,请假天数:" + days;
}
return super.handle(days); // 处理不了,传给下一个
}
}

// 系主任:能批 3 天以内的
public class DepartmentHead extends Handler {
@Override
public String handle(int days) {
if (days <= 3) {
return "系主任审批通过,请假天数:" + days;
}
return super.handle(days);
}
}

// 院长:能批 7 天以内的
public class Dean extends Handler {
@Override
public String handle(int days) {
if (days <= 7) {
return "院长审批通过,请假天数:" + days;
}
return super.handle(days);
}
}

注意 super.handle(days) 这一行。处理不了的时候,调用父类的 handle,父类里会把请求传给 next。这就是链式传递的核心机制。

组装链条

把处理者串起来,形成一条完整的请假审批链:

Handler counselor = new Counselor();
counselor.setNext(new DepartmentHead()).setNext(new Dean());

组装完成后,链条长这样:

请求 → 辅导员 → 系主任 → 院长 → 兜底

使用

调用方只需要把假条交给链头,不需要关心谁来处理:

public class Main {
public static void main(String[] args) {
Handler counselor = new Counselor();
counselor.setNext(new DepartmentHead()).setNext(new Dean());

System.out.println(counselor.handle(1)); // 辅导员审批通过,请假天数:1
System.out.println(counselor.handle(3)); // 系主任审批通过,请假天数:3
System.out.println(counselor.handle(5)); // 院长审批通过,请假天数:5
System.out.println(counselor.handle(14)); // 无法处理当前请假申请
}
}

请 1 天假,辅导员直接批了;

请 3 天假,辅导员批不了,转给系主任,系主任批了;

请 5 天假,辅导员和系主任都批不了,转到院长那里,院长批了;

请 14 天假,整条链走完都没人能批,落到兜底逻辑。

调用方完全不知道中间经过了几个环节。它只管把假条丢给链头,剩下的事情链条自己搞定。

完整代码

把上面的步骤合在一起,我们来看一下完整的实现:

// 抽象处理者
public abstract class Handler {
private Handler next;

public Handler setNext(Handler handler) {
this.next = handler;
return handler;
}

public String handle(int days) {
if (next != null) {
return next.handle(days);
}
return "无法处理当前请假申请";
}
}

// 具体处理者:辅导员
public class Counselor extends Handler {
@Override
public String handle(int days) {
if (days <= 1) {
return "辅导员审批通过,请假天数:" + days;
}
return super.handle(days);
}
}

// 具体处理者:系主任
public class DepartmentHead extends Handler {
@Override
public String handle(int days) {
if (days <= 3) {
return "系主任审批通过,请假天数:" + days;
}
return super.handle(days);
}
}

// 具体处理者:院长
public class Dean extends Handler {
@Override
public String handle(int days) {
if (days <= 7) {
return "院长审批通过,请假天数:" + days;
}
return super.handle(days);
}
}

// 使用
public class Main {
public static void main(String[] args) {
Handler counselor = new Counselor();
counselor.setNext(new DepartmentHead()).setNext(new Dean());

System.out.println(counselor.handle(1)); // 辅导员审批通过,请假天数:1
System.out.println(counselor.handle(3)); // 系主任审批通过,请假天数:3
System.out.println(counselor.handle(5)); // 院长审批通过,请假天数:5
System.out.println(counselor.handle(14)); // 没人能处理这个请假申请
}
}

整个实现的核心机制就三个关键词:持有引用、判断能力、传递请求。每个处理者持有下一个处理者的引用,判断自己能不能处理当前请求,处理不了就调用下一个处理者的 handle 方法。链条的组装和请求的传递,靠这条引用链串起来。在这个例子里,辅导员持有系主任的引用,系主任持有院长的引用,假条沿着这条链一路传下去,谁有权限批准谁就签字,签字之后即退出链条,如果没人有权限那么就走兜底措施。

理解上述责任链模式模板的难点在于 setNext(Handler handler) 和 return super.handle(days)。前者负责把处理者串成一条链,后者负责让请求沿着链条往后传,理解了这两处设计,你就真的看懂了责任链模式。

责任链模式与 if-else 的对比分析

你可能会想:这不就是把 if-else 拆开了吗?本质上有什么区别?

区别很大。来对比一下:

对比维度if-else责任链模式
新增处理者 改已有方法,加 else if 新建一个类,挂到链上(比如新增教务处处长)
删除处理者 删 else if,可能影响后续逻辑 从链上摘掉,不影响其他节点
处理顺序 隐式,靠代码顺序 显式,靠链条组装顺序
职责边界 全堆在一个方法里 每个处理者独立,各管各的
可测试性 难以单独测试某个分支 每个处理者可以独立单元测试
运行时灵活性 编译时写死 可以动态组装、动态调整链条

if-else 是编译时写死的逻辑分支,责任链是运行时可组合的对象链条。

举个实际的差异:如果你的系统需要根据不同的学院配置不同的请假审批流程,if-else 方案几乎不可能做到动态切换。但责任链模式下,你只需要为每个学院组装不同的链条,运行时切换就行:

// 普通学院的请假审批链:辅导员 → 系主任
Handler chainA = new Counselor();
chainA.setNext(new DepartmentHead());

// 重点学院的请假审批链:辅导员 → 系主任 → 院长 → 教务处处长
Handler chainB = new Counselor();
chainB.setNext(new DepartmentHead()).setNext(new Dean()).setNext(new AcademicDean());

// 运行时根据学院选择不同的链
Handler chain = "普通学院".equals(college) ? chainA : chainB;
chain.handle(days);

这种灵活性是 if-else 做不到的。

责任链模式的典型应用场景

责任链模式在实际开发中非常常见,这里举一个非常典型的场景。

场景:Servlet Filter 链

如果你用过 Java Web 开发,那其实你每天都在和责任链打交道。javax.servlet.Filter 就是责任链模式的经典实现。HTTP 请求进入服务器后,会依次经过一系列 Filter:身份认证、权限校验、参数校验、日志记录、限流……每个 Filter 就是一个处理者,它们串成一条链:

HTTP 请求


认证 Filter ──(token 无效,直接返回 401)

▼ (token 有效,继续传递)
权限 Filter ──(权限不足,直接返回 403)

▼ (权限通过,继续传递)
参数校验 Filter ──(参数非法,直接返回 400)

▼ (参数合法,继续传递)
业务 Servlet ──→ 返回 200

每个 Filter 都可以通过 FilterChain.doFilter() 决定:拦截请求(不调 doFilter,直接返回),或者放行请求(调 doFilter,传给下一个)。这就是责任链模式。

来看一个极简的 Filter 实现:

// 模拟 Filter 接口
public interface Filter {
void doFilter(Request request, Response response, FilterChain chain);
}

// 模拟 FilterChain
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index = 0;

public void addFilter(Filter filter) {
filters.add(filter);
}

public void doFilter(Request request, Response response) {
if (index < filters.size()) {
filters.get(index++).doFilter(request, response, this);
}
}
}

// 认证 Filter
public class AuthFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
if (request.getToken() == null) {
response.setStatus(401);
response.setBody("未登录");
return; // 拦截,不调 chain.doFilter
}
// token 有效,放行
chain.doFilter(request, response);
}
}

// 限流 Filter
public class RateLimitFilter implements Filter {
private int count = 0;
private int limit = 100;

@Override
public void doFilter(Request request, Response response, FilterChain chain) {
count++;
if (count > limit) {
response.setStatus(429);
response.setBody("请求过于频繁");
return; // 拦截
}
chain.doFilter(request, response); // 放行
}
}

// 使用
FilterChain chain = new FilterChain();
chain.addFilter(new AuthFilter());
chain.addFilter(new RateLimitFilter());
chain.doFilter(request, response);

注意这里和前面请假场景的区别:请假场景中请求被某个处理者处理后就终止了,但 Filter 链中每个 Filter 都可以选择放行,让请求继续往下走。Servlet 的 FilterChain 用索引(index)来控制链条的推进,比我们自己用 next 引用更直观。

小结

责任链模式本质上是在做一件事:解耦请求的发送者和接收者。发送者不需要知道谁会处理,接收者也不需要知道请求从哪来。两者通过一条链连接,链条的组装可以灵活调整。如果你的代码里出现了越来越长的 if-else 判断链,每次新增逻辑都要回去改老代码,不妨试试把它重构成责任链。先抽出抽象处理者,再把每个分支变成一个具体处理者,最后串成链。就像学校请假审批,把辅导员、系主任、院长各自的责任边界明确下来,假条沿着链条走,谁有权限谁就签字审批,比一堆 if-else 清晰得多,可维护性更好。

当然,责任链也不是万能的。如果处理逻辑简单且固定、几乎不会变更,if-else 反而更加简单直接,实现更加快速。设计模式是工具,不是信仰,也不能为了设计模式而使用设计模式,一切以业务需要为准,用到真正需要的地方。

赞(0)
未经允许不得转载:171主机测评 » 设计模式:责任链模式
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址