设计模式——责任链模式
在阎宏博士的《Java与模式》一书中开头是这样描述责任链(Chain of Responsibility)模式的:
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
责任链模式通俗点说就是一个发起的请求有多个不同的处理者,不同的处理者能力权限不同,通过系统的流程管控,最后到达对应权限的处理者进行处理。即多个对象都可以接收到同一请求,但具体交由哪个对象处理在运行时动态判断决定。
比如,在公司发起请假流程,你填写请假10天,流程发起后,组长一看“我靠,10天的假期,我批不了”,组长把这个消息转发出去,到主管处,主管也处理不了,然后主管转发给部门经理,部门经理审批结束。
在上面的例子,我们并不知道最后是哪位处理了我们的请求,只得到最后的结果,假单被批准了。同样,责任链模式中,客户端并不知道谁最后处理了这个流程,客户端只需要通过外抛的接口进行进行发送请求接口。
UML图
责任链模式涉及的组成角色如下:
- 抽象处理者角色(Handler):抽象定义出处理请求的接口,可由抽象类或接口担当;
- 具体处理者角色(ConcreteHandler):请求的真实执行者,具体处理者接到请求后,可以选择将请求处理掉,或者将请求转发给下一个处理者。
案例场景
我们还是采用上面的假单流程处理作为实例进行解析。
抽象事件处理接口
首先根据需求,抽象出需要处理的的请假流程处理。
/**
* 定义抽象的处理类
* @author Iflytek_dsw
*
*/
abstract class IHandler{
protected IHandler handler;
public abstract boolean handleRequest(int leaveDay);
}
抽象接口中,包含一个Handler的引用,这是建立整个链式关心的核心,因为这样才能让每个处理者之间相互关联。
定义具体的事件处理者
我们的请假流程,会首先提给我们的组长,然后到主管、部门经理这样的一个层级,如果请假1天组长直接审批就ok了,每个审批者对应的权限不同。
/**
* 小组组长
* @author Iflytek_dsw
*
*/
class ChargeHand extends IHandler{
@Override
public boolean handleRequest(int leaveDay) {
if(leaveDay <= 1){
System.out.println("组长处理请假,批准");
return true;
}else{
return handler.handleRequest(leaveDay);
}
}
}
/**
* 主管处理
* @author Iflytek_dsw
*
*/
class ChargeLeader extends IHandler{
@Override
public boolean handleRequest(int leaveDay) {
if(leaveDay <= 5){
System.out.println("主管领导处理请假,批准");
return true;
}else{
return handler.handleRequest(leaveDay);
}
}
}
/**
* 部门经理审批
* @author Iflytek_dsw
*
*/
class DepartmentLeader extends IHandler{
@Override
public boolean handleRequest(int leaveDay) {
if(leaveDay <= 10){
System.out.println("部门经理处理请假,批准");
return true;
}else{
System.out.println("人力资源直接备案,批准");
return true;
}
}
}
链式关系的构造
抽象接口、处理者都已经定义,但是他们还没有形成链式关系,对于客户端来说,他不需要为此构建这种链式关系,也没这个权利。所以我们需要构建这种链式关系。
/**
* 假单流程处理类
* @author Iflytek_dsw
*
*/
public class LeaveProcess {
private static LeaveProcess instance;
private IHandler chargeHandler,leaderHandler,departmentHandler;
private LeaveProcess(){
/**
* 创建对应的处理这,并且构建链式关系
*/
chargeHandler = new ChargeHand();
leaderHandler = new ChargeLeader();
departmentHandler = new DepartmentLeader();
chargeHandler.handler = leaderHandler;
leaderHandler.handler = departmentHandler;
};
public static LeaveProcess getInstance(){
if(instance == null){
synchronized(LeaveProcess.class){
if(instance == null){
instance = new LeaveProcess();
}
}
}
return instance;
}
/**
* 提交给直接的组长进行请假申请
* @param leaveDay 请假天数
* @return
*/
public boolean sendLeaveProcess(int leaveDay){
return chargeHandler.handleRequest(leaveDay);
}
}
在LeaveProcess类中,持有了所有处理者的引用,然后建立他们之间的链式关系,以便这个过程达到“自动”分发处理的效果。
客户端
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
if(LeaveProcess.getInstance().sendLeaveProcess(8)){
System.out.println("请假8天被审批");
}else{
System.out.println("未批准");
}
}
}
这样就封装了整个过程,对于客户端来说,它并不在知道也不必知道谁处理,只需要关心执行结果即可。
优缺点
优点:
- 降低耦合度。它将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增加新的请求处理类很方便。
**缺点: **
- 不能保证请求一定被接收。
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
- 可能不容易观察运行时的特征,有碍于除错。
使用场景:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。