假如有这样一个场景:小红做作业的时候,有一道题不会做,这时候,小红就去问同桌小明怎么做,小明表示也不会做,于是,小红又去问学习委员小黄,小黄也不会做,最后,小红不得不请教老师这个问题,老师给江红讲解了这个问题。
从上述场景中,我们看到小红依次找了 小明、小黄、老师,如果用流程图表示,那么就是:
看起来就像是一条链条,一个请求,在链条上的各个节点间传递,知道请求被响应,这就是职责链模式。
1.职责链模式
职责链模式(Chain of Responsibility): 将能够处理同一类请求的对象连成一条链,使这些对象都有机会处理请求,所提交的请求沿着链传递。从而避免请求的发送者和接受者之间的耦合关系。链上的对象逐个判断是否有能力处理该请求,如果能则就处理,如果不能,则传给链上的下一个对象。直到有一个对象处理它为止。 ----《大话设计模式》
职责链模式的结构图图如下:
这里的关键点,在于链上的节点Handler,持有一个指向下一节点的引用,实现的思想类似于数据结构中的链表。需要注意的是,对于一个请求,请求者和Handler都不知道该请求会在哪一个Handler被处理,每个Handler对象都要充分考虑清楚如何处理,防止请求走到最后一个Handler而无法被处理的情况发生。
2.代码实现
对于开篇的场景,我做了代码的演示。小红提出一个问题,封装为一个请求实体,我采用枚举类Request封装问题,按照难度等级分为不同请求对象:
/** * 请求封装,使用枚举,分为:简单问题,普通问题,困难问题,超级难题 */ enum Request { SIMPLE_QUESTION, NORMAL_QUESTION, HARD_QUESTION, SUPER_HARD_QUESTION }
然后是创建问题解答者的抽象类,RequestHandler,关键点是,此类聚合有本类实体other,代表着“下一个”节点,即问题的传播方向。定义了抽象方法answer,其子类需要override该方法以对请求进行处理。
/** * 问题回答者的抽象类 */ abstract class RequestHandler { protected String name; public RequestHandler(String name) { this.name = name; } protected RequestHandler other; public void setHandler(RequestHandler handler) { other = handler; } public abstract void answer(Request question); }
RequestHandler的三个子类: 分别代表同桌、学习委员、老师, 他们需要实现answer方法,对请求进行处理,注意要考虑周全请求无法处理的情况,也需要合理的结束请求在这条链上传播的过程。
/** * 同桌 */ class Deskmate extends RequestHandler { public Deskmate(String name) { super(name); } @Override public void answer(Request question) { if (Request.SIMPLE_QUESTION == question) { System.out.printf("%s 解答了问题。 ",name); }else{ other.answer(question); } } } /** * 学习委员 */ class StudySecretary extends RequestHandler { public StudySecretary(String name) { super(name); } @Override public void answer(Request question) { if (Request.SIMPLE_QUESTION == question || Request.NORMAL_QUESTION == question) { System.out.printf("%s 解答了问题。 ",name); } else{ other.answer(question); } } } /** * 老师 */ class Teacher extends RequestHandler { public Teacher(String name) { super(name); } @Override public void answer(Request question) { if (Request.SIMPLE_QUESTION == question || Request.NORMAL_QUESTION == question || Request.HARD_QUESTION == question) { System.out.printf("%s 解答了问题。 ",name); } else { System.out.println("问题太难,没人会。"); } } }
最后就是客户端调用,首先创建出三个Hanlder子类的实例, 然后给"同桌"和“学习委员”设置后继者,这样,就由“同桌”、“学习委员”、“老师”三个“节点”连成了一条链。“老师”没有设置后继者,为终点。
问题的提问,我这里从“同桌”开始, 实际情况中,并不一定非得从“起点”开始, 并且节点之间的顺序,也可以改变。
public class ChainDemo { public static void main(String[] args) { // 职责链上的"节点" RequestHandler deskmate = new Deskmate("同桌小明"); RequestHandler studySecretaryre = new StudySecretary("学习委员小黄"); RequestHandler teacher = new Teacher("老师"); // 设置"后继者" deskmate.setHandler(studySecretaryre); studySecretaryre.setHandler(teacher); //从同桌开始提问 deskmate.answer(Request.SIMPLE_QUESTION); deskmate.answer(Request.NORMAL_QUESTION); deskmate.answer(Request.HARD_QUESTION); deskmate.answer(Request.SUPER_HARD_QUESTION); } }
输出结果
同桌小明 解答了问题。
学习委员小黄 解答了问题。
老师 解答了问题。
问题太难,没人会。
3.总结
通过一个场景:“教室里小红问同桌问题,同桌不会问学习委员,学习委员不会问老师”,这样一个生活例子,介绍了职责链模式。职责链模式的优点是降低了请求发送者和请求接收者的耦合度, 因为请求者只需要把问题发送给一个接收者就可以了,不需要关心具体是哪个接收者处理的问题结果。很明显,职责链模式,很适合在流程审批这类场景中应用,比如在公司发出一条请假审批, 不同职位对于员工请假天数的批准,有不同的权限,比如直接主管职能批准2天及以下,总监能批准5天......
职责链模式扩展起来也比较方便,就像数据结构中链表的插入效率很高一个道理,职责链中在某两个节点之间插入一个新的节点,也只需要更改前一个节点的“后继者”而已。
总之,应用任何实际模式,都要按需应用,不可滥用,比如如果流程成环了,就要注意职责链模式是否合适;或者是节点过多,那么以后万一请求变化了,每一个链上的节点都需要修改,这样就不大合适,这种场景,就应该考虑一个“中间人”的角色接收请求再转给其他实际接收者的模式了。