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