责任链模式
责任链模式可以模块化的封装处理逻辑,进行调用方和被调用方的解耦。
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。我们熟悉的Filter、JS的事件冒泡都是责任链设计模式的典型实践。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
责任链模式是一种对象行为型模式,其主要优点如下。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
- 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
其主要缺点如下:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
在使用的时候我们可能会有疑问,似乎创造一群父类相同的对象处理不同的情况并没有比 if-else 高明多少,该写的代码并没有减少。
但我们应该意识到的是,if-else 可能是写在调用方的,这就造成了调用方与被调用方有耦合的太紧,被调用方的改动需要调用方同步改动进行适配。而如果调用方只负责将数据传给被调用方的话,被调用方改动时,调用方不需要做任何变化,做到了调用方与被调用方的解耦。改动的部分由调用方被提拉到了拉起整个调用链条的 Handle ,更加符合单一职责原则,使我们的代码更易维护。
另外,相比 if-else ,每种情况下的处理被我们封装到了不同的类中,在某种情况的处理逻辑发生变化时,我们可以更加方便的对相应的类进行改动,同时我们可以通过装饰器或代理等等其它方式对这些处理逻辑进行复用。也就是我们将处理逻辑模块化了,使整个处理逻辑的结构变的更加灵活。
我们通过一个日志处理的例子来更加直观的感受一下责任链模式的作用:
我们有一个 Map 中记录了日志的级别和信息,我们需要针对不同级别的日志采取不同的处理逻辑,如果使用责任链模式,我们需要先定义一个日志处理的基类:
/** * @Author Nxy * @Date 2020/2/28 22:02 * @Description 日志处理基类 */ public abstract class LogHandle { //下一个处理节点 LogHandle nextHandle; LogHandle(LogHandle nextHandle) { this.nextHandle = nextHandle; } //处理逻辑 public abstract String handler(Map<String, String> logMap); //参数合法性检查 protected void checkUpLogMap(Map<String, String> logMap) { if (logMap == null) { throw new NullPointerException("logMap is null!"); } Set keySet = logMap.keySet(); if (!keySet.contains("level")) { throw new NullPointerException("no key named 'level' exist in logMap"); } if (!keySet.contains("message")) { throw new NullPointerException("no key named 'message' exist in logMap"); } } }
然后将不同级别的处理逻辑封装在其子类中,错误级别的处理逻辑:
public class ErrorLogHandle extends LogHandle { ErrorLogHandle(LogHandle nextHandle) { super(nextHandle); } @Override public String handler(Map<String, String> logMap) { checkUpLogMap(logMap); String level = logMap.get("level"); //日志级别为错误,本类受理 if ("error".equals(level)) { return this.doLog(logMap); } else { String returnStr = this.nextHandle == null ? "no handler accept log with '" + level + "' level" : this.nextHandle.handler(logMap); return returnStr; } } //模拟日志处理方法 protected String doLog(Map<String, String> logMap) {
return "errorLogHandler working" + logMap.get("message"); } }
警告级别的处理逻辑:
public class WanningLogHandler extends LogHandle { WanningLogHandler(LogHandle next) { super(next); } @Override public String handler(Map<String, String> logMap) { this.checkUpLogMap(logMap); String level = logMap.get("level"); //日志级别为错误,本类受理 if ("wanning".equals(level)) { return this.doLog(logMap); } else { String returnStr = this.nextHandle == null ? "no handler accept log with '" + level + "' level" : this.nextHandle.handler(logMap); return returnStr; } } protected String doLog(Map<String, String> logMap) {
return "wanningLogHandler working" + logMap.get("message"); } }
信息级别的处理逻辑:
public class InfoLogHandler extends LogHandle { InfoLogHandler(LogHandle next) { super(next); } @Override public String handler(Map<String, String> logMap) { this.checkUpLogMap(logMap); String level = logMap.get("level"); //日志级别为错误,本类受理 if ("info".equals(level)) { return this.doLog(logMap); } else { String returnStr = this.nextHandle == null ? "no handler accept log with '" + level + "' level" : this.nextHandle.handler(logMap); return returnStr; } } protected String doLog(Map<String, String> logMap) {
return "infoLogHandler working" + logMap.get("message"); } }
拉起责任链条的工厂类:
public class LogHandlerFactory { static LogHandle root = new ErrorLogHandle( new WanningLogHandler( new InfoLogHandler(null) ) ); static String handler(Map<String, String> logMap) { return root.handler(logMap); } }
下面便是调用方的调用,调用方只需要将参数传入工厂类,不需要了解其处理逻辑:
Map<String,String> logMap=new HashMap<String,String>(); logMap.put("level","info"); logMap.put("message"," 'an info' "); String re=LogHandlerFactory.handler(logMap); System.out.print(re);
处理结果:
下面看一下解耦的好处,有一条需求发生变更,日志多了一个 joke 级别,我们需要新增 joke 级别的处理逻辑。如果是 if-else ,我们需要在调用增加一个else了,而责任链模式下则完全不需要修改调用方的逻辑。
我们新增 joke 级别的处理逻辑:
public class JokeLogHandler extends LogHandle { JokeLogHandler(LogHandle next) { super(next); } @Override public String handler(Map<String, String> logMap) { checkUpLogMap(logMap); String level = logMap.get("level"); //日志级别为错误,本类受理 if ("joke".equals(level)) { return this.doLog(logMap); } else { String returnStr = this.nextHandle == null ? "no handler accept log with '" + level + "' level" : this.nextHandle.handler(logMap); return returnStr; } } //模拟日志处理方法 protected String doLog(Map<String, String> logMap) {
return "jokeLogHandler working" + logMap.get("message"); } }
我们只需在拉起链条时增加该节点即可:
public class LogHandlerFactory { static LogHandle root = new ErrorLogHandle( new WanningLogHandler( new InfoLogHandler(new JokeLogHandler(null)) ) ); static String handler(Map<String, String> logMap) { return root.handler(logMap); } }
调用方在完全不知情的情况下即可完成处理逻辑的增加:
调用方从头到尾都只是 LogHandlerFactory.handler(logMap) 没有做过任何改变。
下面我们看一下模块化的好处,我们又接到一条需求变更,需要新增 errorWriteToDB 级别的日志,处理逻辑为 error 级别的基础上增加写入数据库的逻辑,如果是 if-else 写法,该 else 节点需要复制一段与 error 的 else 节点逻辑完全一致的代码。
但现在因为我们把 error 的处理逻辑封装了,因此可以进行复用。我们用装饰器模式对 error 的处理逻辑进行复用:
package learning.chainOfResponsibilityPattern; import java.util.Map; public class ErrorWriteToDBLogHandler extends ErrorLogHandle { ErrorWriteToDBLogHandler(LogHandle nextHandle, ErrorLogHandle errorHandler) { super(nextHandle); this.errorHandler = errorHandler; } ErrorLogHandle errorHandler; @Override public String handler(Map<String, String> logMap) { checkUpLogMap(logMap); String level = logMap.get("level"); //日志级别为错误,本类受理 if ("errorWriteToDB".equals(level)) { return this.doLog(logMap); } else { String returnStr = this.nextHandle == null ? "no handler accept log with '" + level + "' level" : this.nextHandle.handler(logMap); return returnStr; } } //模拟日志处理方法 @Override protected String doLog(Map<String, String> logMap) { String errorRe=errorHandler.doLog(logMap); return errorRe+" and errorWriteToDBLogHandler working" + logMap.get("message");
}
}
在责任链中新增节点:
public class LogHandlerFactory { static LogHandle root = new ErrorLogHandle( new WanningLogHandler( new InfoLogHandler(new JokeLogHandler( new ErrorWriteToDBLogHandler(null, new ErrorLogHandle(null)) )) ) ); static String handler(Map<String, String> logMap) { return root.handler(logMap); } }
处理结果: