前言
这个模式基本是框架必备的一个设计模式,无论是netty还是spring,最为经典的aop也有体现这个设计模式,这个设计模式的学习难度不是很大,也比较容易看到具体的使用方式。
定义
责任链模式可以看作是对于请求和处理的解耦,比如框架之中常用的拦截器就是将请求和处理进行解耦,我们只要按照规范实现同样的接口,同时配置Bean之后就可以像刀片服务器一样“插入”我们自己定义的规则,由于我们设计不同的规则会有不同的拦截效果,所以这种设计模式是一种“行为型模式”。
关于责任链这个设计模式最为常见场景是类似校验,审核或者拦截处理,以及一些文本过滤等等工作的时候经常使用,下面我们来看下他的结构图是如何设计的:
优缺点
优点:
- 解耦了请求的处理者和发送者,处理者不需要关心发送者如何发送,只需要关注和处理自己的逻辑即可。
- 请求的处理顺序可以自由的定义和组合,提高了请求处理代码的复用性。
缺点:
- 责任链可能会丢失请求的处理者,因为责任链不知道谁是请求的处理者。
- 如果责任链过长过多,并且如果中间处理时间长,会影响整个请求和处理的效率
- 关键在于客户端的配置,如果客户端设置有误可能会产生莫名其妙的问题,比如编码处理之后进行字符处理,或者数据处理顺序不对等
第一种实现:统一调度
第一种实现较为简单,也是比较好理解的一种方式,这种处理方式有一点类似于注册监听器之后将请求分发给每一个监听者,
规范接口:规范接口比较常见的为handler()
方法,由子类负责实现接口之后通过责任链调度中心进行调用,统一规范接口。
具体的处理对象:也就是上文所说的请求和处理接耦的的处理方,处理方不需要关心如果调用给
责任处理中心:主要负责责任链的内部调度和传递等等,当然也可以类似拦截处理。
客户端:注册相关的具体拦截处理对象,由责任处理中心统一处理,主要和客户端进行交互调度。
下面我们根据一个简单的加减乘除数字处理的调度器进行责任起模式的模板代码讲述。
请求处理接口:
请求处理接口定义了两个数字参数,同时返回处理结果
public interface Request { /** * 责任链的规范接口 * @param val1 参数1 * @param val1 参数2 * @return */ int process(int val1, int val2); }
调度中心
责任链的调度中心主要负责请求的调度和处理,这里使用了数组的形式维护整个调度的过程。
public class RequestChain { private int index; private Request[] requestList; public RequestChain(int index) { this.index = 0; this.requestList = new Request[index]; } public void addRequest(Request request) { if (index >= requestList.length) { throw new UnsupportedOperationException("已满,无法继续添加"); } requestList[index++] = request; } public boolean process(int val1, int val2) { int result = 0; for (Request req : requestList) { try { result += req.process(val1, val2); } catch (Exception e) { System.out.printf("第%s处理失败,处理失败为%s%n", index, req); return false; } } System.out.printf("处理结果:%s\n", result); return true; } }
具体实现
具体的实现方代码如下,下面是关于加除法和减法的相关处理:
public class DevideRequest implements Request{ @Override public int process(int val1, int val2) { if(0 == val2){ throw new IllegalArgumentException("除数不能为0"); } return new BigDecimal(val1).divide(new BigDecimal(val2)).intValue(); } }
public class AddRequest implements Request{ @Override public int process(int val1, int val2) { return val1 + val2; } }
public class SubstractRequest implements Request { @Override public int process(int val1, int val2) { return val1 - val2; } }
最后是单元测试的相关代码:
public class Main { public static void main(String[] args) { RequestChain requestChain = new RequestChain(3); requestChain.addRequest(new AddRequest()); requestChain.addRequest(new SubstractRequest()); requestChain.addRequest(new DevideRequest()); requestChain.process(15, 6); } }
这部分代码还是比较好理解一些, 下面我们来看下关于责任链模式存在一个“变化”的版本,主要的变动是在调用的处理中心使用类似链表的形式进行切换处理,具体的代码如下。
第二种实现:抽象处理者
抽象处理者和上面的分发形式略有不同,首先调度中心由单独的类改为抽象接口转抽象类的形式处理,通过定义抽象方法的形式,让下层的具体请求处理对象去处理下一次的转发对象。
责任链抽象调度中心:负责定义下层具体实现调度器的转发规则,以及定义统一的请求逻辑处理规范
具体处理调度对象:继承抽象调度中心的同时,需要调用调度中心的方法转发请求处理给下一个处理者,这样就实现了请求处理的转发操作,实现处理方法之后实现自己的处理逻辑。
抽象处理者
抽象处理者替代了责任调用中心,主要提供转发个下一个请求者的方法以及必须由具体实现者实现的一个抽象请求处理方法。
//抽象处理者角色 abstract class Handler { private Handler next; public void setNext(Handler next) { this.next = next; } public Handler getNext() { return next; } //处理请求的方法 public abstract void handleRequest(String request); }
具体实现
具体实现里面作为实现层需要将请求转发给下一个处理者,所以可以发现这样串联之后的请求处理方相互是清楚下一个处理者的,但是不清楚上一个处理者。
//具体处理者角色1 class ConcreteHandler1 extends Handler { public void handleRequest(String request) { if (request.equals("one")) { System.out.println("具体处理者1负责处理该请求!"); } else { if (getNext() != null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } } //具体处理者角色2 class ConcreteHandler2 extends Handler { public void handleRequest(String request) { if (request.equals("two")) { System.out.println("具体处理者2负责处理该请求!"); } else { if (getNext() != null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } }
下面是具体的单元测试代码:
public class ChainOfResponsibilityPattern { public static void main(String[] args) { //组装责任链 Handler handler1 = new ConcreteHandler1(); Handler handler2 = new ConcreteHandler2(); handler1.setNext(handler2); //提交请求 handler1.handleRequest("two"); } }
总结
责任链模式自己编写的情况比较少,但是框架用的比较多,只要多熟悉和责任链模式有关的模版代码即可。
写在最后
常见的设计模式在过去的文章中讲述的差不多了,后续会讲述一些比较偏门的设计模式。