zoukankan      html  css  js  c++  java
  • 【设计模式】责任链模式

    一、前言

      责任链模式很多框架都有用到,其中一个经典场景就是Tomcat对HTTP请求的处理。
      Tomcat处理HTTP请求时就会处理请求头和请求体两部分,当然,Tomcat的真正实现会将HTTP请求切分成更细的部分进行处理。如果请求各部分的逻辑都在一个类中实现,这个类会非常臃肿。如果请求通过增加新字段完成升级,则接受者需要添加处理新字段的处理逻辑,这就需要修改该类的代码,不符合“开放-封闭”原则。
      责任链模式就可以很好地处理上述问题,将上述完整的、臃肿的接受者的实现逻辑拆分到多个只包含部分逻辑的、功能单一的Handler处理类中,开发人员可以根据业务需求将多个Handler对象组合成一条责任链,实现请求的处理。在一条责任链中,每个Handler对象都包括对下一个Handler对象的引用,一个Handler对象处理完请求消息(或不能处理该请求)时,会把请求传给下一个Handler对象继续处理,以此类推,直至整条责任链结束。
    责任链模式的类图如下:

     

    enter description here
    enter description here

    优点:
    1.弱化了发出请求的人和处理请求的人之间的关系
    2.可以动态的改变责任链,删除或者添加或者改变顺序。
    3.让各个处理者专注于实现自己的职责
    缺点
    1.如果构造的责任链变成了环形结构,进行代码调试和定位问题比较困难
    2.推卸责任也可能导致处理延迟

     

    二、初始版实现

    需求是给定一个日志级别,不高于这个日志级别的日志记录器都会打印自己的日志信息。
    第一步:创建抽象日志处理器
    AbstractLogger

    public abstract class AbstractLogger {
    
      public static int INFO = 1;
      public static int DEBUG = 2;
      public static int ERROR = 3;
    
      protected int level;
    
      // 责任链中的下一个元素
      protected AbstractLogger nextLogger;
    
      public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
      }
    
      public void logMessage(int level, String message) {
        if (this.level <= level) {
          write(message);
        }
        if (nextLogger != null) {
          nextLogger.logMessage(level, message);
        }
      }
    
      abstract protected void write(String message);
    }
    

    第二步:具体日志处理器
    ConsoleLogger

    public class ConsoleLogger extends AbstractLogger {
    
      public ConsoleLogger(int level){
        this.level = level;
      }
    
      @Override
      protected void write(String message) {
        System.out.println("Standard Console::Logger: " + message);
      }
    }
    

    ErrorLogger

    public class ErrorLogger extends AbstractLogger {
    
      public ErrorLogger(int level){
        this.level = level;
      }
    
      @Override
      protected void write(String message) {
        System.out.println("Error Console::Logger: " + message);
      }
    }
    

    FileLogger

    public class FileLogger extends AbstractLogger {
    
      public FileLogger(int level) {
        this.level = level;
      }
    
      @Override
      protected void write(String message) {
        System.out.println("File::Logger: " + message);
      }
    }
    

    ChainPatternDemo

    public class ChainPatternDemo {
    
      public static void main(String[] args) {
        AbstractLogger loggerChain = getChainOfLoggers();
        loggerChain.logMessage(AbstractLogger.INFO,
            "This is an information.");
    
        loggerChain.logMessage(AbstractLogger.DEBUG,
            "This is an debug level information.");
    
        loggerChain.logMessage(AbstractLogger.ERROR,
            "This is an error information.");
    
      }
    
      private static AbstractLogger getChainOfLoggers(){
        ErrorLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        FileLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        ConsoleLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
    
        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);
        return errorLogger;
      }
    
      
    
    }
    

    运行Main方法,测试结果如图。

     

    enter description here
    enter description here

     

    三、升级版实现

    上面的方式实现了功能,但是不够优雅,还需要手动调用setXX方法来设置引用,于是有了下面升级版的实现。
    类图如下:

     

    enter description here
    enter description here

    FilterChain

     

    public class FilterChain extends AbstractLoggerFilter {
    
      private int pos;
    
      private List<AbstractLoggerFilter> loggerFilterList;
    
    
      public void addFilter(AbstractLoggerFilter loggerFilter) {
        if (loggerFilterList == null) {
          loggerFilterList = new ArrayList<>();
        }
        loggerFilterList.add(loggerFilter);
      }
    
      @Override
      void doFilter(int level, FilterChain filterChain) {
        if (pos == loggerFilterList.size()) {
          return;
        }
        filterChain.loggerFilterList.get(pos++).doFilter(level, filterChain);
      }
    
    
    }
    
    

    这个过滤器链有两个属性,pos用于表示当前过滤器在过滤器集合中的角标,loggerFilterList是过滤器的集合。
    doFilter() 方法先判断是否全部处理完了,如果不是就交给下个处理器去处理,递归调用。

    AbstractLoggerFilter

    public abstract class AbstractLoggerFilter {
    
      abstract void doFilter(int level,FilterChain filterChain);
    }
    

    ConsoleLoggerFilter

    public class ConsoleLoggerFilter extends AbstractLoggerFilter {
    
      private LoggerInfo loggerInfo;
    
      public ConsoleLoggerFilter(LoggerInfo loggerInfo){
        this.loggerInfo = loggerInfo;
      }
    
      @Override
      void doFilter(int level, FilterChain filterChain) {
        if (loggerInfo.getLevel() <= level){
          System.out.println("Standard Console::Logger: " + loggerInfo.getMessage());
        }
        filterChain.doFilter(level,filterChain);
      }
    }
    

    ErrorLoggerFilter

    public class ErrorLoggerFilter extends AbstractLoggerFilter {
    
      private LoggerInfo loggerInfo;
    
      public ErrorLoggerFilter(LoggerInfo loggerInfo){
        this.loggerInfo = loggerInfo;
      }
    
      @Override
      void doFilter(int level, FilterChain filterChain) {
        if (loggerInfo.getLevel() <= level){
          System.out.println("Error Console::Logger: " + loggerInfo.getMessage());
        }
        filterChain.doFilter(level,filterChain);
      }
    }
    

    FileLoggerFilter

    public class FileLoggerFilter extends AbstractLoggerFilter {
    
      private LoggerInfo loggerInfo;
    
      public FileLoggerFilter(LoggerInfo loggerInfo){
        this.loggerInfo = loggerInfo;
      }
    
      @Override
      void doFilter(int level, FilterChain filterChain) {
        if (loggerInfo.getLevel() <= level){
          System.out.println("File::Logger: " + loggerInfo.getMessage());
        }
        filterChain.doFilter(level,filterChain);
      }
    }
    

    实体类LoggerInfo

    public class LoggerInfo {
    
      private int level;
    
      private String message;
    
      public LoggerInfo(int level, String message) {
        this.level = level;
        this.message = message;
      }
    
    //省略get/set方法
    }
    

    客户端LoggerFilterDemo

    public class LoggerFilterDemo {
    
      public static int INFO = 1;
      public static int DEBUG = 2;
      public static int ERROR = 3;
    
      public static void main(String[] args) {
        ConsoleLoggerFilter consoleLoggerFilter = new ConsoleLoggerFilter(
            new LoggerInfo(INFO, "This is an information."));
        ErrorLoggerFilter errorLoggerFilter = new ErrorLoggerFilter(
            new LoggerInfo(ERROR, "This is an error information."));
        FileLoggerFilter fileLoggerFilter = new FileLoggerFilter(
            new LoggerInfo(DEBUG, "This is an debug level information."));
        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(consoleLoggerFilter);
        filterChain.addFilter(errorLoggerFilter);
        filterChain.addFilter(fileLoggerFilter);
    
        filterChain.doFilter(ERROR, filterChain);
      }
    
    }
    
    

    如果以后还需要添加新类型的Logger,只需要new出来添加到FilterChain中就可以了。
    运行测试类,可以看到所有类型的Logger都打印了日志。

    Standard Console::Logger: This is an information.
    Error Console::Logger: This is an error information.
    File::Logger: This is an debug level information.
    

    代码地址。

  • 相关阅读:
    C++头文件编译问题
    Windows下Critical Section、Event、Mutex、Semaphores区别
    MFC OnCtlColor函数用来改变控件颜色
    Ctreectrl +CImageList应用
    20091113 08:37 实现文件读写操作的几种方法
    在网上看到这篇文章还不错,OnDrawItem与DrawItem讨论
    Mysql全文搜索match…against的用法
    Django and fulltext search
    Feed DB 搜博客 一个博客搜索引擎
    通过扩展Django数据库API支持全文搜索
  • 原文地址:https://www.cnblogs.com/2YSP/p/11482845.html
Copyright © 2011-2022 走看看