设计模式——职责链模式

 更多内容,前往 IT-BLOG

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工差旅费的报销,可审批的领导有部门负责人、副总经理、总经理等,但每个领导能批准的金额不同,员工必须根据自己要批准的金额去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。
在计算机软硬件中也有相关例子,例如异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,如果用责任链模式都能很好解决。

职责链模式(Chain of Responsibility Pattern):又叫责任链模式,为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

一、模式的结构


职责链模式主要包含以下角色
    抽象处理者(Handler)角色:定义一个处理请求的抽象类或接口,包含抽象处理方法和自己(传入下一个子类时赋值)。
    具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
    请求对象(Request):包含很多请求属性。
    客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
    

二、职责链模式的应用案例


学校 OA 系统的采购审批项目:采购员采购教学器材费用审批规则如下:
 ■  如果金额 小于等于 10000,有学校主任审批;
 ■  如果金额 小于等于 30000,由院长审批;
 ■  如果金额 大于 30000,由校长审批;

【1】请求者类(普通类):包含两个属性(编号:id 和价格:price)

 1 public class Request {
 2         //请求编号
 3     private int id;
 4         //请求价格
 5     private float price;
 6     /**
 7      * @param id
 8      * @param price
 9      */
10     public Request(int id, float price) {
11         super();
12         this.id = id;
13         this.price = price;
14     }
15     public int getId() {
16         return id;
17     }
18     public float getPrice() {
19         return price;
20     }
21 }

【2】 抽象处理者(Handler)类:特点是包含了一个自己的属性,有点像装饰者模式。用于传入自己的子类,通过 set方法。还有一个抽象的业务处理方法 process。

 1 public abstract class Handler {
 2     //组合一个自己,并创建一个 set 方法,由子类使用
 3     Handler handler;
 4     //处理者的名称
 5     String name;
 6     
 7     /**
 8      * @param name
 9      * 构造器,传入名称即可
10      */
11     public Handler(String name) {
12         super();
13         this.name = name;
14     }
15 
16     public void setHandler(Handler handler) {
17         this.handler = handler;
18     }
19     
20     //业务处理方法,抽象的
21     public abstract void process(Request request);
22 }

【3】具体处理者(Concrete Handler)类:主任审批类。存在多个相似的类,都继承自抽象处理者。实现抽象的 process 方法,如果能处理则处理,否则抛给其他子类。

 1 public class CollegeHandler extends Handler{
 2     
 3     private static final float PRICE_1 = 10000f;
 4     
 5     public CollegeHandler(String name) {
 6         super(name);
 7     }
 8     
 9     //如果金额 小于等于 10000,有学校主任审批
10     @Override
11     public void process(Request request) {
12         if(request.getPrice() <= PRICE_1) {
13             System.out.println("请求编号: "+request.getId()+"被"+name + "处理");
14         }else {
15             handler.process(request);
16         }
17     }
18 }

【3.1】 具体处理者(Concrete Handler)类:院长审批。与上述的住人审批基本相同,区别就是业务处理方法 process 的处理条件不同而已。处理不了就抛给父类的 Handler 对象。校长审批与之类似省略。。。

 1 public class DepartmentHandler extends Handler{
 2     
 3     private static final float PRICE_1 = 10000f;
 4     private static final float PRICE_3 = 30000f;
 5     
 6     public DepartmentHandler(String name) {
 7         super(name);
 8     }
 9     
10     //如果金额 小于等于 30000,由院长审批;
11     @Override
12     public void process(Request request) {
13         if(request.getPrice() >  PRICE_1 && request.getPrice() <=  PRICE_3) {
14             System.out.println("请求编号: "+request.getId()+"被"+name + "处理");
15         }else {
16             handler.process(request);
17         }
18     }
19 }

【4】客户类(Client)类:将请求对象和处理类进行关联。特点是将多个处理类的关系进行了链式组装。代码如下:

 1 public class Client {
 2     public static void main(String[] args) {
 3         //创建请求对象
 4         Request request = new Request(1, 300001);
 5         //创建各个处理者
 6         DepartmentHandler departmentHandler = new DepartmentHandler("学校主任");
 7         CollegeHandler collegeHandler = new CollegeHandler("院长");
 8         SchoolMasterHandler schoolMasterHandler = new SchoolMasterHandler("校长");
 9         //对处理者的链进行组装(注意:这里组装成一个环形)
10         //可能有人认为,会不会出现死循环,这个应该是业务代码考虑的问题。如果有不能处理的情况应该自行抛出错误
11         departmentHandler.setHandler(collegeHandler);
12         collegeHandler.setHandler(schoolMasterHandler);
13         schoolMasterHandler.setHandler(departmentHandler);
14         
15         //可以随便调用一个处理者来处理对象
16         collegeHandler.process(request);
17         departmentHandler.process(request);
18         /** 输出展示:请求编号: 1被校长处理
19          *  请求编号: 1被校长处理
20          */
21     }
22 }

三、职责链模式的特点


职责链模式的优点:1)、降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
2)、增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
3)、增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
4)、责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
5)、责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

职责链模式的缺点:1)、不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
2)、对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
3)、职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

四、模式的应用场景


前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用。
【1】有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
【2】可动态指定一组对象处理请求,或添加新的处理者。
【3】在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

五、模式的扩展


职责链模式存在以下两种情况
【1】纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。也就是上面的例子。
【2】不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。在上面的else中添加部分的业务逻辑处理。

六、源码分析


职责链模式在 SpringMVC 框架应用中有使用。接下来就对 SpringMVC 的HandleExceptionChain 类进行分析。

上述图和下述代码说明:SpringMVC 请求的流程图中,执行了拦截器方法 intercepto.preHandler 等等。在处理 SpringMVC 请求时,使用到了职责链模式和适配器模式。HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是它本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链的实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程。HandlerExecutionChain 维护了 HandlerInterceptor 的集合,可以向其中注册相应的拦截器。

 1 public class DispatcherServlet extends FrameworkServlet {
 2     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 3         HandlerExecutionChain mappedHandler = null;
 4         //获取到 HandlerExecutionChain 对象
 5         mappedHandler = getHandler(processedRequest);
 6         
 7         //...... 
 8         //重点:如果执行成功则,直接返回,否则执行下一个 Handler
 9         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
10             return;
11         }
12         
13         //上述 applyPreHandle 方法的实现
14         boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
15             if (getInterceptors() != null) {
16                 for (int i = 0; i < getInterceptors().length; i++) {
17                     HandlerInterceptor interceptor = getInterceptors()[i];
18                     //当 preHandle 链不能处理时,调用 triggerAfterCompletion 链
19                     if (!interceptor.preHandle(request, response, this.handler)) {
20                         //triggerAfterCompletion 内部也是获取所有的拦截器,并调用 afterCompletion
21                         triggerAfterCompletion(request, response, null);
22                         return false;
23                     }
24                     this.interceptorIndex = i;
25                 }
26             }
27             return true;
28         }
29         
30         //当 handler 不符合 PreHandler 链时,执行 PostHandler 链
31         mappedHandler.applyPostHandle(processedRequest, response, mv);
32         void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
33             for (int i = getInterceptors().length - 1; i >= 0; i--) {
34                 HandlerInterceptor interceptor = getInterceptors()[i];
35                 interceptor.postHandle(request, response, this.handler, mv);
36             }
37         }
38         
39         //上述 triggerAfterCompletion 的源码
40         void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
41             throws Exception {
42 
43             for (int i = this.interceptorIndex; i >= 0; i--) {
44                 HandlerInterceptor interceptor = getInterceptors()[i];
45                 try {
46                     interceptor.afterCompletion(request, response, this.handler, ex);
47                 }
48                 catch (Throwable ex2) {
49                     logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
50                 }
51             }
52         }
53     }
54 }


----关注公众号,获取更多内容----

posted @ 2020-11-19 11:19  Java程序员进阶  阅读(8)  评论(0编辑  收藏  举报