鸣谢:《大话设计模式》

简介

是一种行为型设计模式

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

我理解的责任链模式核心就是Handler抽象类中组合了自己,但这个this.handler和自己并不一定相等,可以通过setNextHandler方法设置下一个责任人,使得接受者发送者都没有对方明确信息,且身在庐山中的责任人也不用知道庐山的面貌

主要组成部分:

  • 抽象处理者(Handler):抽象处理者定义了处理请求的接口,通常包含一个处理请求的方法。它还可以维护一个指向下一个处理者的引用。

  • 具体处理者(Concrete Handler):具体处理者是抽象处理者的实现,负责实际处理请求。如果可以处理请求,它将处理请求并停止传递;否则,它将请求传递给链中的下一个处理者。

  • 客户端(Client):客户端创建并将请求传递给责任链的第一个处理者。客户端通常不需要知道链中的具体处理者,只需将请求发送给链的起始点。

工作流程

  • 客户端创建具体处理者并将它们连接成一个链。通常,客户端将链中的处理者按照某种顺序连接,以定义请求的处理顺序。

  • 客户端将请求发送给链中的第一个处理者。每个处理者尝试处理请求,如果它能够处理,则停止传递请求;否则,将请求传递给链中的下一个处理者。

  • 请求在链中传递,直到某个处理者处理请求或者请求到达链的末端而未被处理。


例子

结构

代码

其他类

/**
 * @author: lpy
 * @Date: 2023/11/04
 */
@Data
@Accessors(chain = true)
public class Request {
    private String name;
    private Integer num;
    private Integer level;
}

抽象处理者(Handler)

/**
 * @author: lpy
 * @Date: 2023/11/04
 */
public abstract class Handler {
    protected String name;
    public Handler(String name){
        this.name = name;
    }
    // 这是核心,要组合自己,但this.handler和自己并不一定相等
    protected Handler handler;

    /**
     * nextHandler
     * @param handler
     */
    public Handler setNextHandler(Handler handler){
        this.handler = handler;
        return handler;
    }

    public abstract void deal(Request req);

}

具体处理者(Concrete Handler)

/**
 * @author: lpy
 * @Date: 2023/11/04
 */
public class PreHandler extends Handler{
    public PreHandler(String name) {
        super(name);
    }

    @Override
    public void deal(Request req) {
        if (req.getLevel()>5){
            System.out.println(name+"无权处理");
            if (handler!=null){
                this.handler.deal(req);
            }
        }else{
            System.out.println(name+"处理"+req.toString());
            if (handler!=null){
                this.handler.deal(req);
            }
        }
    }
}
/**
 * @author: lpy
 * @Date: 2023/11/04
 */
public class MiddleHandler extends Handler {
    public MiddleHandler(String name) {
        super(name);
    }

    @Override
    public void deal(Request req) {
        System.out.println(name + "处理" + req.toString());
        if (handler != null) {
            this.handler.deal(req);
        }
    }
}
/**
 * @author: lpy
 * @Date: 2023/11/04
 */
public class LastHandler extends Handler {
    public LastHandler(String name) {
        super(name);
    }

    @Override
    public void deal(Request req) {
        System.out.println(name + "处理" + req.toString());
        if (handler != null) {
            this.handler.deal(req);
        }
    }
}

客户端(Client)

/**
 * @author: lpy
 * @Date: 2023/11/04
 */
public class TestResponsibility {
    public static void main(String[] args) {
        Handler preHandler = new PreHandler("预处理");
        Handler middleHandler = new MiddleHandler("中处理");
        Handler lastHandler = new LastHandler("后处理");
        preHandler.setNextHandler(middleHandler).setNextHandler(lastHandler);

        Request req = new Request().setLevel(5).setName("请求1").setNum(2);
        preHandler.deal(req);

        Request req2 = new Request().setLevel(6).setName("请求2").setNum(4);
        preHandler.deal(req2);
    }
}

应用

  • 审批流程:企业中的审批流程通常涉及多个层级的审批人,请求依次传递给不同的审批人,直到某人批准或拒绝。每个审批人可以视情况决定是否继续传递请求。

  • 异常处理:在软件开发中,异常处理可以采用责任链模式。异常从底层传递到顶层,每个处理者负责检查异常类型并决定是否处理异常或将其传递给下一个处理者。

  • 日志记录:在日志记录中,可以使用责任链模式来过滤和记录不同级别的日志信息。不同的处理者可以根据日志级别决定是否记录日志。

  • 网络请求处理:在网络服务器应用中,可以使用责任链模式来处理不同类型的请求。请求可以依次传递给不同的处理者,每个处理者负责处理特定类型的请求。

  • 洗牌算法:在扑克游戏中,洗牌算法可以使用责任链模式。每个处理者负责一次洗牌操作,请求依次传递给下一个处理者,以完成整个洗牌过程。

  • 权限验证:在应用程序中,可以使用责任链模式来进行权限验证。不同的处理者可以检查用户的不同权限级别,以确定是否允许执行特定操作。

  • 请求过滤器:Web应用程序中的请求过滤器可以采用责任链模式,每个过滤器负责对请求进行不同类型的检查和处理。

  • 商品优惠策略:在线商店中,根据不同的促销策略和商品类型,可以使用责任链模式来计算商品的最终价格。每个处理者可以应用不同的优惠规则。

源码中的影子

生物的多样性啊,下面源码用的请求参数传递

javax.servlet.Filter接口有一个doFilter方法,FilterChain有一个doFilter方法,Filter的实现有很多,就像下面的LoggerContextFilter类的doFilter,chain.doFilter(request, response)

public interface Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    public default void destroy() {}
}

public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;
}
public class LoggerContextFilter implements Filter {

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // ...省略代码
            // 看这里,交给下一个链条处理    
            chain.doFilter(request, response);
    }
}
public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        // ...省略代码
        // 看这里,交给下一个链条处理    
        filterChain.doFilter(request, response);
    }

}

责任链模式优缺点

  • 优点:
    • 解耦合:责任链模式将请求的发送者与接收者解耦,使客户端不需要知道链中的具体处理者。这提高了系统的灵活性和可维护性。

    • 扩展性:可以轻松添加新的处理者到链中,而无需修改现有代码。

    • 动态调整处理顺序:可以动态调整处理者的顺序,以满足不同的请求处理需求。

    • 单一职责原则:每个处理者只负责处理特定类型的请求,符合单一职责原则。

  • 缺点:

    • 性能影响:由于责任链模式中的请求依次经过多个处理者,因此在链中的每个处理者都会对请求进行处理,这可能会导致性能损耗。在一些高性能应用中,这种性能开销可能不可接受。

    • 不保证请求被处理:如果责任链中没有适当的处理者来处理请求,或者链中的处理者配置错误,请求可能会到达链的末端而没有得到处理。这需要开发人员仔细设计和配置责任链,以确保所有请求都能得到妥善处理。

    • 复杂性增加:责任链模式可能导致链中的处理者数量增加,从而增加了代码的复杂性。维护和调试可能会变得更加困难,尤其是在大型链中。

    • 难以理解:责任链模式的处理流程可能分散在多个对象中,导致代码难以理解和维护。阅读代码时,开发人员需要跟踪请求的传递路径,这可能会增加理解难度。

    • 潜在的循环问题:如果责任链配置不当,可能会导致请求在链中无限循环。开发人员需要小心设计责任链,以避免这种情况。

    • 不容易查找责任处理者:在链中的某个特定处理者可能不容易找到,特别是在大型链中。这可能使调试和维护变得困难。

对比

责任链模式 与 状态模式 比较 :

在 责任链模式 中 , 并 不指定 下一个处理的 请求对象 是哪个 ; 责任链 中 链条顺序 可以 任意组合排序 ;

在 状态模式 中 , 需要让 每个 状态 知道下一个需要处理的状态是谁 ;

7 大设计原则

不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;

- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。

- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。

- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。

- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。

- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。

- 迪米特法则(Law of Demeter - LoD):
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。

源码位置

全部源码github.com/xs-alpha/designPattern

参考文献

如有内容侵权,麻烦联系博主删除

浙公网安备33011302000604

辽ICP备20003309号