欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

简说设计模式——职责链模式

程序员文章站 2022-03-07 10:10:53
一、什么是职责链模式 从文字角度出发,我们可以先将关注点放在“链”字上,很容易联想到链式结构,举个生活中常见的例子,击鼓传花游戏就是一个很典型的链式结构,所有人形成一条链,相互传递。而从另一个角度说,职责链就是所谓的多级结构,比如去医院开具病假条,普通医生只能开一天的证明,如果需要更多时常,则需将开 ......

一、什么是职责链模式

  从文字角度出发,我们可以先将关注点放在“链”字上,很容易联想到链式结构,举个生活中常见的例子,击鼓传花游戏就是一个很典型的链式结构,所有人形成一条链,相互传递。而从另一个角度说,职责链就是所谓的多级结构,比如去医院开具病假条,普通医生只能开一天的证明,如果需要更多时常,则需将开具职责转交到上级去,上级医师只能开三天证明,如需更多时常,则需将职责转交到他的上级,以此类推,这就是一个职责链模式的典型应用。再比如公司请假,根据请假时常的不同,需要递交到的级别也不同,这种层级递进的关系就是一种多级结构。

  职责链模式(chain of responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。uml结构图如下:

简说设计模式——职责链模式

  其中,handler是抽象处理者,定义了一个处理请求的接口;concretehandler是具体处理者,处理它所负责的请求,可访问它的后继者,如果可处理该请求就处理,否则就将该请求转发给它的后继者。

  1. 抽象处理者

  抽象处理者实现了三个职责:

  • 定义一个请求的处理方法handlermessage(),是唯一对外开放的方法
  • 定义一个链的编排方式setnext(),用于设置下一个处理者
  • 定义了具体的请求者必须实现的两个方法,即定义自己能够处理的级别的gethandlerlevel()方法及具体的处理任务echo()方法
 1 public abstract class handler {
 2     
 3     private handler nexthandler;    //下一个处理者
 4     
 5     public final response handlermessage(request request) {
 6         response response = null;
 7         
 8         if(this.gethandlerlevel().equals(request.getrequestlevel())) {    //判断是否是自己的处理级别
 9             response = this.echo(request);
10         } else {
11             if(this.nexthandler != null) {    //下一处理者不为空
12                 response = this.nexthandler.handlermessage(request);
13             } else {
14                 //没有适当的处理者,业务自行处理
15             }
16         }
17         
18         return response;
19     }
20     
21     //设定下一个处理者
22     public void setnext(handler handler) {
23         this.nexthandler = handler;
24     }
25     
26     //每个处理者的处理等级
27     protected abstract level gethandlerlevel();
28     
29     //每个处理者都必须实现的处理任务
30     protected abstract response echo(request request);
31 
32 }

  2. 具体处理者

  这里我们定义三个具体处理者,以便能组成一条链,concretehandlerb及concretehandlerc就不再赘述了。

 1 public class concretehandlera extends handler {
 2 
 3     @override
 4     protected level gethandlerlevel() {
 5         //设置自己的处理级别
 6         return null;
 7     }
 8 
 9     @override
10     protected response echo(request request) {
11         //完成处理逻辑
12         return null;
13     }
14 
15 }

  3. level类

  level类负责定义请求和处理级别,具体内容需根据业务产生。

1 public class level {
2     //定义一个请求和处理等级
3 }

  4. request类

  request类负责封装请求,具体内容需根据业务产生。

1 public class request {
2     
3     //请求的等级
4     public level getrequestlevel() {
5         return null;
6     }
7     
8 }

  5. response类

  response类负责封装链中返回的结果,具体内容需根据业务产生。

1 public class response {
2     //处理者返回的数据
3 }

  6. client客户端

  我们在场景类或高层模块中对类进行组装,并传递请求,返回结果。如下对三个具体处理者进行组装,按照1→2→3的顺序,并得出返回结果。

 1 public class client {
 2     
 3     public static void main(string[] args) {
 4         handler handler1 = new concretehandlera();
 5         handler handler2 = new concretehandlerb();
 6         handler handler3 = new concretehandlerc();
 7         
 8         //设置链中的阶段顺序 1->2->3
 9         handler1.setnext(handler2);
10         handler2.setnext(handler3);
11         
12         //提交请求返回结果
13         response response = handler1.handlermessage(new request());
14     }
15 
16 }

  当然这是个未完成的模板,最终结果会因为 request.getrequestlevel() 为空而抛出异常,具体内容需根据业务逻辑进行编写。

 二、职责链模式的应用

  1. 何时使用

  • 处理消息时

  2. 方法

  • 拦截的类都实现同一接口

  3. 优点

  • 将请求和处理分开,实现解耦,提高系统的灵活性
  • 简化了对象,使对象不需要知道链的结构

  4. 缺点

  • 性能会收到影响,特别是在链比较长的时候
  • 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  • 不能保证请求一定被接收

  5. 使用场景

  • 有多个对象可以处理同一个请求
  • 在不明确指定接收者的情况下,向多个对象中的提交请求
  • 可动态指定一组对象处理请求

  6. 应用实例

  • 多级请求
  • 击鼓传花
  • 请假/加薪请求
  • java web中tomcat对encoding的处理、拦截器

  7. 注意事项

  • 需控制链中最大节点数量,一般通过在handler中设置一个最大节点数量,在setnext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能

三、职责链模式的实现

  我们就以请假/加薪为例,实现一个较为简单的职责链模式。uml图如下:

简说设计模式——职责链模式

  1. 抽象管理者

  通过manager抽象类管理所有管理者,setsuperior()方法用于定义职责链的下一级,即定义当前管理者的上级。

 1 public abstract class manager {
 2     
 3     protected string name;
 4     protected manager superior;    //管理者的上级
 5     
 6     public manager(string name) {
 7         this.name = name;
 8     }
 9     
10     //设置管理者的上级
11     public void setsuperior(manager superior) {
12         this.superior = superior;
13     }
14     
15     //申请请求
16     public abstract void handlerrequest(request request);
17 
18 }

  2. 具体管理者

  经理类如下,只可批准两天以内的假期,其余请求将继续申请上级。

 1 public class commonmanager extends manager {
 2 
 3     public commonmanager(string name) {
 4         super(name);
 5     }
 6 
 7     @override
 8     public void handlerrequest(request request) {
 9         if (request.getrequesttype().equals("请假") && request.getnumber() <= 2) {    //只能批准两天内的假期
10             system.out.println(name + ":" + request.getrequestcontent() + ",时长:" + request.getnumber() + "天,被批准");
11         } else {    //其余请求申请上级
12             if (superior != null) {
13                 superior.handlerrequest(request);
14             }
15         }
16     }
17 
18 }

  总监类如下,只可批准五天以内的假期,其余请求将继续申请上级。

 1 public class majordomo extends manager {
 2 
 3     public majordomo(string name) {
 4         super(name);
 5     }
 6 
 7     @override
 8     public void handlerrequest(request request) {
 9         if (request.getrequesttype().equals("请假") && request.getnumber() <= 5) {    //只能批准五天内的假期
10             system.out.println(name + ":" + request.getrequestcontent() + ",时长:" + request.getnumber() + "天,被批准");
11         } else {    //其余请求申请上级
12             if (superior != null) {
13                 superior.handlerrequest(request);
14             }
15         }        
16     }
17 
18 }

  总经理类,可以批准任意时常的假期,并且可以批准是否加薪。

 1 public class generalmanager extends manager {
 2 
 3     public generalmanager(string name) {
 4         super(name);
 5     }
 6 
 7     @override
 8     public void handlerrequest(request request) {
 9         if (request.getrequesttype().equals("请假")) {    //能批准任意时长的假期
10             system.out.println(name + ":" + request.getrequestcontent() + ",时长:" + request.getnumber() + "天,被批准");
11         } else if (request.getrequesttype().equals("加薪") && request.getnumber() <= 500) {
12             system.out.println(name + ":" + request.getrequestcontent() + ",金额:¥" + request.getnumber() + ",被批准");
13         } else if (request.getrequesttype().equals("加薪") && request.getnumber() > 500) {
14             system.out.println(name + ":" + request.getrequestcontent() + ",金额:¥" + request.getnumber() + ",再说吧");
15         }
16     }
17 
18 }

  3. 申请类

 1 public class request {
 2     
 3     private string requesttype;    //申请类别
 4     private string requestcontent;    //申请内容
 5     private int number;    //数量
 6 
 7     public string getrequesttype() {
 8         return requesttype;
 9     }
10 
11     public void setrequesttype(string requesttype) {
12         this.requesttype = requesttype;
13     }
14 
15     public string getrequestcontent() {
16         return requestcontent;
17     }
18 
19     public void setrequestcontent(string requestcontent) {
20         this.requestcontent = requestcontent;
21     }
22 
23     public int getnumber() {
24         return number;
25     }
26 
27     public void setnumber(int number) {
28         this.number = number;
29     }
30     
31 }

   4. client客户端

  下面测试几组数据。

 1 public class client {
 2     
 3     public static void main(string[] args) {
 4         commonmanager commonmanager = new commonmanager("尼古拉斯·经理");
 5         majordomo majordomo = new majordomo("尼古拉斯·总监");
 6         generalmanager generalmanager = new generalmanager("尼古拉斯·总经理");
 7         
 8         //设置上级
 9         commonmanager.setsuperior(majordomo);
10         majordomo.setsuperior(generalmanager);
11         
12         request request = new request();
13         request.setrequesttype("请假");
14         request.setrequestcontent("adam请假");
15         request.setnumber(1);
16         commonmanager.handlerrequest(request);
17         
18         request request2 = new request();
19         request2.setrequesttype("请假");
20         request2.setrequestcontent("adam请假");
21         request2.setnumber(4);
22         commonmanager.handlerrequest(request2);
23         
24         request request3 = new request();
25         request3.setrequesttype("加薪");
26         request3.setrequestcontent("adam请求加薪");
27         request3.setnumber(500);
28         commonmanager.handlerrequest(request3);
29         
30         request request4 = new request();
31         request4.setrequesttype("加薪");
32         request4.setrequestcontent("adam请求加薪");
33         request4.setnumber(1000);
34         commonmanager.handlerrequest(request4);
35     }
36 
37 }

   运行结果如下:

  简说设计模式——职责链模式

 

  源码地址:https://gitee.com/adamjiangwh/gof