14.1 请假流程
学生生病了,需要请假,写了请假条交给班长处理。假如此时有三种情况:
1)是小病需要请假时间短,班长能够处理,就批准请假,否则交给老师处理;
2)老师实际查看学生的情况,如果可以处理,就批准请假,如果不能处理,则需要请示校长;
3)校长处理那些老师处理不了的请假,给予学生请假批准。
这位,一个学生的请假流程就算完成了。
14.2 模式定义
责任链模式(Chain of Responsibility Pattern),是一种对象的行为模式。在责任链模式中,很多对象由每一个对象对其下家的引用而连接起来形成一条链。客户端应用请求在这个链上进行传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
责任链模式涉及的角色如下:
1)抽象处理者角色(Handler):定义出一个处理请求的接口。如果需要,接口可以定义出一个方法,以设定和返回下家的引用。这个角色通常由一个Java抽象类或Java接口实现。
2)具体处理者角色(ConcreteHandler):具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于处理者持有下家引用,因此,如果需要,具体处理者可以访问下家。
14.3 普通抽象化实现
14.3.1 学生请假实现
1. 学生接口——IStudent
首先,我们创建学生请假接口IStudent,该接口类中含有两个接口方法:一个是获得学生病情状态,另一个是获取学生请假内容。
package com.demo.chain.message; /** * Created by lsq on 2018/3/26. * 学生接口 */ public interface IStudent { /** * 获得学生病情状态 * 0:小事(班长就可以处理) * 1:班长处理不了,老师才能处理 * 2:老师处理不了,校长才能处理 */ public int getState(); /** * 获得学生请假消息 * @return */ public String getRequestMessage(); }
2. 学生实现——Student
package com.demo.chain.message; /** * Created by lsq on 2018/3/26. * 学生实现类 */ public class Student implements IStudent{ //病情的状态 private int state = -1; //请假消息 private final String message; public Student(int state, String message) { this.state = state; this.message = message; } //获得学生请假状态 @Override public int getState() { return this.state; } //获得学生请假消息 @Override public String getRequestMessage() { return this.message; } }
在学生请假实现类中,定义了两个私有属性:病情状态和请假内容。通过构造方法传入。
14.3.2 创建抽象请假消息处理者
1. 抽象处理者接口——IHandler
package com.demo.chain.handle; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 抽象处理者接口 */ public interface IHandler { //处理请求 public void handleRequest(IStudent student); }
2. 抽象处理者——AbstractHandler
package com.demo.chain.handle; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 抽象处理者 */ public abstract class AbstractHandler implements IHandler { //处理请求,交由子类负责进行具体的处理 public abstract void process(IStudent student); //处理请求 public void handleRequest(IStudent student){ //如果学生对象存在 if (student!=null){ this.process(student); } } }
14.3.3 请假处理者具体实现
1. 班长——SquadLeaderHandler
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 班长(处理者) */ public class SquadLeaderHandler extends AbstractHandler{ //子类处理具体请求 @Override public void process(IStudent student) { System.out.println("班长 批复:"+student.getRequestMessage()); } }
2. 老师——TeacherHandler
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 老师(处理者) */ public class TeacherHandler extends AbstractHandler { //子类处理具体请求 @Override public void process(IStudent student) { System.out.println("老师 批复:"+student.getRequestMessage()); } }
3. 校长——SchoolMasterHandler
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 校长(处理者) */ public class SchoolMasterHandler extends AbstractHandler{ //子类具体处理请求 @Override public void process(IStudent student) { System.out.println("校长 批复:"+student.getRequestMessage()); } }
14.3.4 客户端测试
package com.demo.chain; import com.demo.chain.handle.IHandler; import com.demo.chain.impl.SchoolMasterHandler; import com.demo.chain.impl.SquadLeaderHandler; import com.demo.chain.impl.TeacherHandler; import com.demo.chain.message.IStudent; import com.demo.chain.message.Student; import java.util.Random; /** * Created by lsq on 2018/3/26. * 主应用程序 */ public class Client { public static void main(String[] args) { //班长处理者 IHandler sqmshandler = new SquadLeaderHandler(); //老师处理者 IHandler techhandler = new TeacherHandler(); //校长处理者 IHandler scmshandler = new SchoolMasterHandler(); //创建随机数对象,用来随机产生学生对象 Random random = new Random(); for (int i=0;i<3;i++){ //获得随机数 int radom = random.nextInt(3); IStudent student = new Student(radom, "学生"+i+"生病了,要请假!"); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); //处理消息 if (student.getState()==0){ //班长就能处理 sqmshandler.handleRequest(student); }else { System.out.println("请求上级领导批复!"); if (student.getState()==1){ //老师能处理 techhandler.handleRequest(student); }else { System.out.println("请求上级领导批复!"); if (student.getState()==2){ //校长能处理 scmshandler.handleRequest(student); } } } System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "); } } }
运行结果:
14.3.5 如何面对变化
程序是可以正常运行了,但是,它符合我们的设计原则吗?首先,“开-闭”原则就不可以。如果,现在需要新增加一个具体的处理者,我们岂不是要修改客户端应用程序,增加一个 else if?很显然,上面的程序做不到对扩展开放,对修改关闭。如何完善程序结构呢?想想,迄今为止,我们已经学习了许多的设计模式,是否可以在此使用呢?我们需要封装那些变化的部分,具体的处理者就是变化的部分,可能会增加,我们需要抽象和封装。
14.3.6 使用外观封装变化部分
package com.demo.chain.process; import com.demo.chain.handle.IHandler; import com.demo.chain.impl.SchoolMasterHandler; import com.demo.chain.impl.SquadLeaderHandler; import com.demo.chain.impl.TeacherHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 外观类,处理学生请假请求 */ public class ProcessHandler { //单例模式 private static ProcessHandler processHandler = new ProcessHandler(); //班长处理者 private final IHandler sqmshandler; //老师处理者 private final IHandler techhandler; //校长处理者 private final IHandler scmshandler; /** * 构造方法建立请假处理责任链 */ private ProcessHandler(){ //创建处理对象 this.sqmshandler = new SquadLeaderHandler(); this.techhandler = new TeacherHandler(); this.scmshandler = new SchoolMasterHandler(); } /** * 获得单例对象实例 * @return */ public static ProcessHandler getInstance(){ return processHandler; } /** * 发送请假请求 * @param student */ public void sendMessage(IStudent student){ //处理消息 if (student.getState()==0){ //班长就能处理 sqmshandler.handleRequest(student); }else { System.out.println("请求上级领导批复!"); if (student.getState()==1){ //老师能处理 techhandler.handleRequest(student); }else { System.out.println("请求上级领导批复!"); if (student.getState()==2){ //校长能处理 scmshandler.handleRequest(student); } } } } }
在客户端应用程序中,使用外观模式来处理学生的请假请求。修改后的Client客户端应用程序如下:
package com.demo.chain; import com.demo.chain.message.IStudent; import com.demo.chain.message.Student; import com.demo.chain.process.ProcessHandler; import java.util.Random; /** * Created by lsq on 2018/3/26. * 主应用程序 */ public class Client2 { public static void main(String[] args) { //获得外观对象 ProcessHandler processHandler = ProcessHandler.getInstance(); //创建随机数对象,用来随机产生学生对象 Random random = new Random(); for (int i=0;i<3;i++){ //获得随机数 int radom = random.nextInt(3); IStudent student = new Student(radom, "学生"+i+"生病了,要请假!"); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); //处理消息 processHandler.sendMessage(student); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "); } } }
运行程序,结果如下:
外观模式很好地为我们封装了处理对象,封装了对学生请假请求的处理,客户端应用中不再有if…else…等语句,做到了对扩展开放,对修改关闭。
外观的增加很好地解决了客户端分支判断的麻烦,然而,我们仅仅是将复杂的条件判断“搬移”到了外观对象中,并没有做任何的改变,客户端只不过由原来的复杂判断,到调用外观做复杂的逻辑判断处理,换汤不换药,系统结构没有发生根本的改变。
我们仔细分析学生请假的流程结构,会发现这其实是一个链条式的请求传递,客户端应用请求在这个链上进行传递,直到链上的某一个对象决定处理此请求为止,这就是一个典型的责任链模式。下面,我们就用责任链模式重新实现上述问题。
14.4 责任链模式分析方法
14.4.1 如何实现消息传递
首先,我们需要肯定的一点是,系统的整体结构是不需要变动的,因为,我们前期已经使用模式来设计了,目前的结构已经封装、抽象的比较完备了,不足的一点就在消息的处理行为上。责任链的实现关键在于消息在链条上的传递,如何实现消息的传递呢?
1)在处理者中要含有链条中下一节点的引用。在抽象处理者AbstractHandler中增加一个IHandler类型的私有属性,用于保存对下家处理者的引用。
2)每一个具体的消息处理者都应该含有一个处理消息的标记位,用来标识当前处理者含有处理消息的级别。这样,我们需要在抽象处理者中增加处理消息的级别状态信息,然后,每一个具体的消息处理者在实例化的时候,初始化该标识,用于鉴别处理消息的权限。
3)处理消息的方法需要链条化。也就是说,消息处理者调用的handleRequest方法需要修改为链条化的处理结构。在handleRequest方法中,首先判断处理权限,如果当前节点能够处理,则处理请求,否则,交给下一个处理者处理。
14.5 请假流程的责任链模式实现
14.5.1 抽象化修改
1. 在IHandler中增加下一个处理者
修改处理接口IHandler,增加设置下一个处理接口方法。
package com.demo.chain.handle; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 抽象处理者接口 */ public interface IHandler { //处理请求 public void handleRequest(IStudent student); //设置下一个处理者 public void setHandler(IHandler handler); }
2. 在AbstractHandler中存储下一个处理者引用,并进行消息传递处理
修改抽象处理者AbstractHandler的内容,增加处理标识位和下一个处理者私有属性,实现设置下一个处理者接口方法,修改handleRequest消息处理方法,使其支持将消息传递给下一个处理者。
package com.demo.chain.handle; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 抽象处理者 */ public abstract class AbstractHandler implements IHandler { //下一个处理者 private IHandler handler; //请假级别 private int state = -1; //构造方法,设置级别 public AbstractHandler(int state){ this.state = state; } //处理请求,交由子类负责进行具体的处理 public abstract void process(IStudent student); //处理请求 public void handleRequest(IStudent student){ //如果学生对象存在 if (student!=null){ if (this.state==student.getState()){ //如果请假级别和当前一致,则当前对象进行处理 this.process(student); }else { if (this.handler!=null){ System.out.println("请求上级领导批复!"); //如果当前对象处理不了,则交给下一个处理者进行处理 this.handler.handleRequest(student); } } } } //设置下一个处理者 public void setHandler(IHandler handler){ this.handler = handler; } }
14.5.2 完善具体消息处理者
1. 班长类
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 班长(处理者) */ public class SquadLeaderHandler extends AbstractHandler{ public SquadLeaderHandler() { super(0); } //子类处理具体请求 @Override public void process(IStudent student) { System.out.println("班长 批复:"+student.getRequestMessage()); } }
2. 老师类
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 老师(处理者) */ public class TeacherHandler extends AbstractHandler { public TeacherHandler(){ super(1); } //子类处理具体请求 @Override public void process(IStudent student) { System.out.println("老师 批复:"+student.getRequestMessage()); } }
3. 校长类
package com.demo.chain.impl; import com.demo.chain.handle.AbstractHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 校长(处理者) */ public class SchoolMasterHandler extends AbstractHandler{ public SchoolMasterHandler(){ super(2); } //子类具体处理请求 @Override public void process(IStudent student) { System.out.println("校长 批复:"+student.getRequestMessage()); } }
在具体的处理者实现类中,我们分别设置了各自的处理权限:班长——0,老师——1,校长——2,见增加的构造方法。
14.5.3 在外观中设置消息处理责任链
package com.demo.chain.process; import com.demo.chain.handle.IHandler; import com.demo.chain.impl.SchoolMasterHandler; import com.demo.chain.impl.SquadLeaderHandler; import com.demo.chain.impl.TeacherHandler; import com.demo.chain.message.IStudent; /** * Created by lsq on 2018/3/26. * 外观类,处理学生请假请求 */ public class ProcessHandler { //单例模式 private static ProcessHandler processHandler = new ProcessHandler(); //班长处理者 private final IHandler sqmshandler; //老师处理者 private final IHandler techhandler; //校长处理者 private final IHandler scmshandler; /** * 构造方法建立请假处理责任链 */ private ProcessHandler(){ //创建处理对象 this.sqmshandler = new SquadLeaderHandler(); this.techhandler = new TeacherHandler(); this.scmshandler = new SchoolMasterHandler(); /** * 建立责任链 */ //设置班长的下一个处理者是老师 this.sqmshandler.setHandler(this.techhandler); //设置老师的下一个处理者是校长 this.techhandler.setHandler(this.scmshandler); } /** * 获得单例对象实例 * @return */ public static ProcessHandler getInstance(){ return processHandler; } /** * 发送请假请求 * @param student */ public void sendMessage(IStudent student){ //发送给第一个处理者:班长处理 this.sqmshandler.handleRequest(student); } }
在外观对象中,我们首先在单例加载时建立责任链结构,设置班长的下一个处理者是老师,老师的下一个处理者是校长,然后,在发送学生请假消息的时候,将消息传递给链条的第一个处理者:班长。之后,消息将在责任链上进行传递,直到有一个处理者处理请求。
14.5.4 客户端测试
客户端程序不需要作任何改动。测试结果相同。
14.6 使用场合
1)有多个对象处理同一个请求,具体由哪一个来处理还不确定,只有在运行时才能确定哪个对象处理的情况。
2)消息具有多个接收者,而接收对象又是不明确的情况。只需要向其中的一个对象发出消息,由其内部具体处理。
3)同一个消息的多个处理对象可能会动态增加或者减少,需要动态地指定的情况。
扩展1:Java SDK 中的责任链模式
在JDK中,也存在着这样的责任链模式,如java.lang.ClassLoader。在java.lang.ClassLoader抽象类中,存在一个指向父类的指针parent,在构造方法中传入父类对象引用,在loadClass方法中就体现了责任链的处理过程。