【設計模式】責任鏈模式

1、前言

  責任鏈模式不少框架都有用到,其中一個經典場景就是Tomcat對HTTP請求的處理。
  Tomcat處理HTTP請求時就會處理請求頭和請求體兩部分,固然,Tomcat的真正實現會將HTTP請求切分紅更細的部分進行處理。若是請求各部分的邏輯都在一個類中實現,這個類會很是臃腫。若是請求經過增長新字段完成升級,則接受者須要添加處理新字段的處理邏輯,這就須要修改該類的代碼,不符合「開放-封閉」原則。
  責任鏈模式就能夠很好地處理上述問題,將上述完整的、臃腫的接受者的實現邏輯拆分到多個只包含部分邏輯的、功能單一的Handler處理類中,開發人員能夠根據業務需求將多個Handler對象組合成一條責任鏈,實現請求的處理。在一條責任鏈中,每一個Handler對象都包括對下一個Handler對象的引用,一個Handler對象處理完請求消息(或不能處理該請求)時,會把請求傳給下一個Handler對象繼續處理,以此類推,直至整條責任鏈結束。
責任鏈模式的類圖以下:html

 

enter description here
enter description here

優勢:
1.弱化了發出請求的人和處理請求的人之間的關係
2.能夠動態的改變責任鏈,刪除或者添加或者改變順序。
3.讓各個處理者專一於實現本身的職責
缺點
1.若是構造的責任鏈變成了環形結構,進行代碼調試和定位問題比較困難
2.推卸責任也可能致使處理延遲

 

2、初始版實現

需求是給定一個日誌級別,不高於這個日誌級別的日誌記錄器都會打印本身的日誌信息。
第一步:建立抽象日誌處理器
AbstractLoggerjava

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);
}

第二步:具體日誌處理器
ConsoleLoggergit

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);
  }
}

ErrorLoggergithub

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);
  }
}

FileLoggerbash

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方法,測試結果如圖。ide

 

enter description here
enter description here

 

3、升級版實現

上面的方式實現了功能,可是不夠優雅,還須要手動調用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() 方法先判斷是否所有處理完了,若是不是就交給下個處理器去處理,遞歸調用。this

AbstractLoggerFilterspa

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.

代碼地址。

相關文章
相關標籤/搜索