精选文集Java开发

责(设计模式)任链模式:代码世界里的击鼓传花

2026-03-01  本文已影响0人  _浅墨_

责任链模式:代码世界里的击鼓传花

假如你在做一个 OA 系统的请假审批功能。

需求文档摆在桌上,写得清清楚楚:请假不超过 3 天,主任审;3 到 10 天,经理审;10 到 30 天,总经理审;超过 30 天,一律拒绝。

刚开始写代码的时候,我们会毫不犹豫地打开if-else

public void approve(int days) {
    if (days < 3) {
        director.approve();
    } else if (days < 10) {
        manager.approve();
    } else if (days < 30) {
        generalManager.approve();
    } else {
        System.out.println("拒绝!");
    }
}

写完一看,逻辑没毛病。

但产品经理走过来说:「以后可能要加副总裁审批,超过 30 天的让他来处理。」

想必我们的表情会当场垮了。


问题出在哪里

表面上代码能跑,本质上耦合死了

审批逻辑全集中在一个方法里,每加一个层级,就要改这段代码。每次改动,都可能引入新的 bug。更糟的是:负责审批的人(Director、Manager、GeneralManager)根本不知道彼此的存在,却被一个外部方法强行调度。

这违反了一个基本原则:每个处理者应该只知道"我能不能处理",以及"处理不了就交给谁"。

这,就是责任链模式要解决的问题。


责任链模式是什么

GoF 给出的定义很简洁:

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

翻译成人话:把处理者串成一条链,请求从头开始一个个往后传,谁能处理谁接手,处理不了就继续传。

类图长这样:

Client ──→ Handler(抽象)
              │  successor(指向下一个)
              ├── ConcreteHandler1
              └── ConcreteHandler2

每个 ConcreteHandler 持有一个 successor 指针,指向链上的下一个处理者。这个结构有点像链表——每个节点只认识自己的下一个,不需要了解整条链。


用责任链重写假条审批

先定义抽象处理者,它只做两件事:持有后继者、声明处理方法。

public abstract class Approver {
    protected String name;
    protected Approver successor; // 后继者,指向链上的下一个

    public Approver(String name) {
        this.name = name;
    }

    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    // 子类各自实现:我能处理吗?不能就往后传
    public abstract void approve(int days);
}

三个具体处理者,每个只关心自己的审批权限:

// 主任:只处理 3 天以内的请假
public class Director extends Approver {
    public Director() { super("主任"); }

    @Override
    public void approve(int days) {
        if (days < 3) {
            System.out.println(name + " 审批通过,请假 " + days + " 天");
        } else if (successor != null) {
            successor.approve(days); // 超出权限,往上传
        }
    }
}

// 经理:处理 3~10 天
public class Manager extends Approver {
    public Manager() { super("经理"); }

    @Override
    public void approve(int days) {
        if (days >= 3 && days < 10) {
            System.out.println(name + " 审批通过,请假 " + days + " 天");
        } else if (successor != null) {
            successor.approve(days);
        }
    }
}

// 总经理:处理 10~30 天,超出则直接拒绝
public class GeneralManager extends Approver {
    public GeneralManager() { super("总经理"); }

    @Override
    public void approve(int days) {
        if (days >= 10 && days < 30) {
            System.out.println(name + " 审批通过,请假 " + days + " 天");
        } else {
            System.out.println("请假 " + days + " 天,超出审批权限,申请拒绝!");
        }
    }
}

组装责任链:

public class Client {
    public static void main(String[] args) {
        Approver director = new Director();
        Approver manager = new Manager();
        Approver general = new GeneralManager();

        // 组链:主任 → 经理 → 总经理
        director.setSuccessor(manager);
        manager.setSuccessor(general);

        // 所有请求都从链头进入,不需要关心谁来处理
        director.approve(2);   // 主任审批通过,请假 2 天
        director.approve(7);   // 经理审批通过,请假 7 天
        director.approve(20);  // 总经理审批通过,请假 20 天
        director.approve(35);  // 请假 35 天,超出审批权限,申请拒绝!
    }
}

现在,产品经理来说要加副总裁了——完全不慌。

// 新增一个处理者,改一下链的组装方式,其他代码一行不动
Approver vp = new VicePresident();
general.setSuccessor(vp); // 总经理 → 副总裁

开闭原则:对扩展开放,对修改关闭。这才是正确的姿势。


责任链不是万能的

有个问题是:责任链"不可以"做到的事是什么?

答案是:高效率地处理请求。

责任链本质上是顺序遍历。最坏情况下,一个请求要走完整条链才能找到处理者,或者根本无人处理。它换来的是灵活性和解耦,代价是效率。

如果你有一个需求,要求精准匹配、直接命中处理者,那应该用策略模式或者简单的 Map 路由,而不是责任链。

用模式,要知道它的边界。


工作中它长什么样

责任链在框架层面其实无处不在,只是换了个名字:

Spring Security 的过滤器链

请求进来
  → UsernamePasswordAuthenticationFilter
  → BasicAuthenticationFilter
  → ExceptionTranslationFilter
  → FilterSecurityInterceptor
  → 到达 Controller

每个 Filter 都可以选择:处理后继续传、处理后中断、直接拦截返回。这是工业级的责任链实现。

Servlet Filter

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        // 前置处理(可以在这里做权限校验、日志记录)
        System.out.println("请求进来了");

        chain.doFilter(req, res); // 传给链上的下一个

        // 后置处理
        System.out.println("响应出去了");
    }
}

FilterChain 就是那根链,chain.doFilter() 就是在说:我处理完了,传给下一个吧。

Netty 的 ChannelPipeline 也是同样的设计,所有入站出站事件都沿 pipeline 流动。


识别责任链的信号

读需求文档的时候,如果你看到这些描述,几乎可以断定是责任链的场景:


最后说一句

古代官府有个制度叫「层层上报」——小事县令决,大事知府定,军国大事上达天听。每一级官员只需知道:这件事我能不能拍板,不能就往上递。

千年前的行政智慧,被我们封装进了一个设计模式。

代码的世界里,好的设计从来不是凭空发明的。它们藏在历史的褶皱里,等着被重新发现。


2026.03.02 12:06
沪 · 赵巷

上一篇 下一篇

猜你喜欢

热点阅读