162°

设计模式之责任链模式

简介

责任链模式(Chain of Responsibility Pattern)属于设计模式的行为型模式。责任链模式与多米诺骨牌有点类似,请求在链中从前向后传递,一直到最后一个。当然责任链的处理可以复杂的多。

定义

责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者与请求处理者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

可以看到在标准的责任链中,请求是只由一个对象来处理的。不过我们不用拘泥于这个定义,责任链的主要目的是将请求处理的各个对象解耦出来,至于在链中传递时有多少个对象处理了请求,其实并不重要。

角色

从定义中可以看出,责任链模式包含的角色有有发送者,请求处理接口,具体请求处理实现及将实现连接起来的链。

发送者:产生请求对象,发起对链的调用
请求处理接口: 定义处理的标准接口,所有的具体请求处理实现都需要实现这个接口,链只需要依赖接口即可
具体请求处理实现:对请求进行处理,并决定是否继续向后传递请求
链:将所有的请求组装到一起,变成一个有序的处理链条

责任链模式可简单可复杂,一般来说接口和实现是必须的,链对象可以简单变为一个 List,处理顺序也可以简化为 for 循环与发送者耦合到一起。如果处理比较复杂,像 Servlet Filter,网络请求库的Handler等框架则会有一个链对象,而且链中的下一级调用与否是由 handler 来决定的。

模式说明

在日志打印框架中,我们会定义很多日志级别,不同级别的日志logger我们可以使用责任链模式来串联起来。

//日志级别
public enum LogLevel {
    DEBUG, INFO, WARN, ERROR
}

@FunctionalInterface public interface Logger { abstract void log(String msg, LogLevel level);

//utitlity methods
default void debug(String msg) {
    log(msg, LogLevel.DEBUG);
}

default void info(String msg) {
    log(msg, LogLevel.INFO);
}

default void warn(String msg) {
    log(msg, LogLevel.WARN);
}

default void error(String msg) {
    log(msg, LogLevel.ERROR);
}

default Logger appendNext(Logger logger) {
    return (msg, level) -> {
        //call self
        log(msg, level);
        //call next Logger
        logger.log(msg, level);
    }
}

}

public class LoggerFactory { public static Logger consoleLogger(LogLevel[] levels) { EnumSet<LogLevel> set = EnumSet.copyOf(Arrays.asList(levels)); return (msg, level) { if (set.contains(level)) { System.out.println("log to console: " + msg); } } }

public static Logger fileLogger(LogLevel[] levels) {
    EnumSet&lt;LogLevel&gt; set = EnumSet.copyOf(Arrays.asList(levels));
    return (msg, level) {
        if (set.contains(level)) {
            System.out.println("log to file: " + msg);
        }
    }
}

public static Logger emailLogger(LogLevel[] levels) {
    EnumSet&lt;LogLevel&gt; set = EnumSet.copyOf(Arrays.asList(levels));
    return (msg, level) {
        if (set.contains(level)) {
            System.out.println("log to email: " + msg);
        }
    }
}

}

/**

  • Logger user */ public class Application { public static void main(String[] args) { Logger logger = LoggerFactory.consoleLogger(LoggerLevel.values() .appendNext(LoggerFactory.fileLogger(new LogLevel[]{LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR}) .appendNext(LoggerFactory.emailLogger(new LogLevel[]{LogLevel.ERROR});

     logger.debug("log will only show in console logger");
     logger.info("log will be printed in console and file loggers");
     logger.warn("log will show in console and file loggers");
     logger.error("log will show in console, file and email loggers");
    

    } }

使用场景

  • 一个请求需要被多次处理,将处理过程解耦的场景(Logger)
  • 有多级处理,由前面的处理结果来决定后续逻辑是否继续(Filter,Handler)
  • 现实场景中的级联处理情况,像在公司中审批从下往上审批(workflow)
  • 责任链是一个从前到后的处理流程,这与我们平常学习到的面向过程变成是一致的,因此函数的处理流程都可以抽象成责任链模式

优点

  • 处理解耦,众多的处理逻辑不是集中在一起使用 if..ele 来判断,而是由各自的类来决定如何处理
  • 符合开闭原则,增加处理逻辑不用修改其他的类,只需要增加到处理链条上
  • 符合最少知识原则。没有一个类负责所有的判断,每个处理类只负责自己的相关部分即可

缺点

  • 处理逻辑分散在很多类中,造成调试困难
  • 处理逻辑在不同的类中有时很改变请求对象,这就必须要小心维护链中处理对象的顺序
  • 请求处理分散,不能保证请求一定被处理
  • 链的构造要小心,可能造成死循环
  • 类过多,增加了函数调用,会造成性能损失

责任链的变种

纯的责任链一般要求请求只会被一个处理对象处理,也就是说链中的对象要么处理请求,要么调用下一个对象。要求太严格,只起到了解耦逻辑的作用。

日常使用的一般都是不纯的责任链(期待以后这变成责任链的正确定义),每个处理对象处理一部分逻辑,连起来变成整个逻辑。而且每个对象也不一定要传递给下一级,而是终止请求处理(拦截器Filter,Interceptor,Handler都是这种形式)。上面的日志处理例子也是不纯的责任链。

责任链使用中一般都是前后相连的一根链条,这是为了简单。链也可以变成树或有向图,也就是每个处理对象可以由多个后继,增加处理的灵活性(没见过)。

最佳实践

  • 链一般是由客户端或其他方法创建的,可以与设计模式的创建型模式相结合
  • 上面我们使用了接口,也可以使用抽象类来定义模版方法,决定是调用下一级还是使用当前处理。子类只需实现判断条件和处理逻辑即可,链交由父类模板
  • 责任链模式是一种工作流思想,可以认为是一种语言级别的工作流实现。如果流程太过复杂,责任链模式是有短板的,这时可以考虑引入工作流引擎。

本文由【52iSilence7】发布于开源中国,原文链接:https://my.oschina.net/liufq/blog/3133107

全部评论: 0

    我有话说: