设计模式之 ==> 责任链计模式
一、什么是责任链设计模式
责任链设计模式(Chain of Responsibitity)为了避免请求的发送者和接收者之间的耦合关系,使多个对象都有机会处理请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
发出请求的客户端并不知道最终是哪个对象处理了这个请求,这样系统内部的更改可以在不影响到客户端的情况下动态的重新组织和分配责任。
二、责任链设计模式写法举例
我们设计这样一个场景:
- 我们要做一个mock服务,根据请求的 URI 来匹配存放返回数据的文件的目录或文件名
- 如果根据 URI 匹配到的是文件,就直接返回文件中的内容(文件只有一个)
- 如果根据 URI 匹配到的是目录,则目录下文件中各项权重的计算来返回某个文件中的内容(文件有多个)
我们使用责任链设计模式来实现:
首先,定义一个抽象类
public abstract class AbstractHandler<T, R> { private AbstractHandler<T, R> nextHandler; public void setNextHandler(AbstractHandler<T, R> nextHandler) { this.nextHandler = nextHandler; } protected abstract boolean preHandle(T context); protected abstract R onHandle(T context) throws Exception; public R doHandle(T context){ if (Objects.isNull(context)) { throw new IllegalArgumentException("context should not be null."); } if (preHandle(context)) { try { return onHandle(context); } catch (Exception e) { throw new IllegalStateException("on handle failed.", e); } } if (!Objects.isNull(this.nextHandler)){ return this.nextHandler.doHandle(context); } throw new IllegalStateException("un know next handler."); } }
该抽象类中有两个抽象方法 prehandle() 和 onhandler(),分别代表前置处理和正式处理,结合前面的业务场景来讲就是 prehandle() 用于来判断是匹配到的是文件还是目录,而onhandler() 则是根据 prehandle() 的判断结果来使用对应的业务逻辑来处理。doHandle() 则是来控制我们整个责任链的业务逻辑的方法,如果 prehandle() 为 true,则执行 onhandler() 中的业务逻辑,如果 prehandle() 为 false,则使用责任链上的下一个节点来再执行一遍这个过程。
接下来是三个抽象类的子类,分别代表文件、目录和其他异常情况的处理逻辑
public class SingletonMappingHandler extends AbstractHandler<MockContext,String> { @Override protected boolean preHandle(MockContext context) { return FileUtil.isFile(context.getMockFilePath()); } @Override protected String onHandle(MockContext context) throws Exception { return IoUtil.read(new FileInputStream(new File(MockerConsts.ROOT_PATH + context.getMockFileName())), Charset.defaultCharset()); } }
如果 URI 匹配到的是文件,则直接读取文件内容返回
public class MultipleMappingHandler extends AbstractHandler<MockContext, String> { @Override protected boolean preHandle(MockContext context) { return FileUtil.isDirectory(context.getMockFilePath()); } @Override protected String onHandle(MockContext context) { if (context.getRequestParam().size() == 0) { return "数据目录下存在多个匹配的mock文件,请根据需要带上必要的参数"; } return ObserverManager.newInstance.getMockData(context); } }
如果 URI 匹配到的是目录,则根据目录下所有文件的权重计算将权重最高的文件内容返回(这里没有体现计算过程)
public class OtherMappingHandler extends AbstractHandler<MockContext, String> { @Override protected boolean preHandle(MockContext context) { return true; } @Override protected String onHandle(MockContext context) { try { FileUtil.listFileNames(context.getMockFilePath()); }catch (IORuntimeException e){ return "数据目录下没有匹配的mock文件!!"; } throw new IllegalStateException(""); } }
这里描述的是除了文件和目录,也有可能匹配不到文件的异常情况的处理逻辑。这里即匹配不到文件,也匹配不到目录,所以 prehandle() 直接设为 true,从而执行 onhandle()中的业务逻辑
最后,是一个维护这个责任链的一个管理类,同时,这个类向外暴露调用方法
public enum ChainManager { /** * 枚举单例 */ newInstance; private AbstractHandler<MockContext, String> mappingHandler; ChainManager() { this.mappingHandler = initMappingHandler(); } private AbstractHandler<MockContext, String> initMappingHandler() { AbstractHandler<MockContext, String> singletonMatchHandler = new SingletonMappingHandler(); AbstractHandler<MockContext, String> multipleMatchHandler = new MultipleMappingHandler(); AbstractHandler<MockContext, String> otherMatchHandler = new OtherMappingHandler(); singletonMatchHandler.setNextHandler(multipleMatchHandler); multipleMatchHandler.setNextHandler(otherMatchHandler); return singletonMatchHandler; } public String doMapping(MockContext context) { return this.mappingHandler.doHandle(context); } }
可以看到,这个 Manager 类是一个单例的枚举类,在构造方法中进行了这个调用链的初始化,同时向外暴露了一个 doMapping() 方法用于外部的调用,这样做的好处是:
- 使用单例的枚举类,这样我们外部不管如何调用,三个业务逻辑处理的对象永远也是单例的。
- 初始化方法 initMappingHandler() 中把链的构造描述清楚了,这样就不用每次在外部调用时去构造这个链了,外部就只需要调 doMapping() 方法就可以了。