责任链设计模式
简单介绍
View的事件分发机制是责任链(Chain of Responsibility)设计模式的典型应用,其它经典的应用场景还有:JavaWeb 的过滤器、拦截器,Servlet中的请求响应链;okhttp开源库中也是在网络层、应用层中使用拦截器来进行分层解耦,使的网络层的配置和开发变得简单而优雅。
案例分析
设计模式并不是一个抽象的概念,而是在编程的实践中总结出的用于解决某类典型问题的典型范式。可以说,设计模式是为实战而生的。这里说的责任链设计模式也不例外,下面看一个真实的应用场景:
员工请假流程,普通员工三天以内的请假申请,TeamLeader直接可以直接审批通过;三到五天的请假申请由TeamLeader审批通过后技术总监(CTO)可以审批通过;五到十天的请假申请就必须得到技术总监(CTO)的审批通过后部门经理(PV)可以审批通过;而十天以上的请假申请就必须得到部门经理(PV)和总经理(CEO)的审批通过后才能正常休假。
员工休假申请,只需要找到自己的直接上级申请就可以了;直接上级可以拒绝员工的请假,如果请假时长超出直接上级的审批范围,直接上级可以向上提交该员工的请假申请。经过具有批准权限的两级的批准,员工才可以正常休假。
实例流程,如下所示:
程序员提交了了角色为【程序员-小林】提交的休假请求;时长:【2】天。
项目组长拦截了角色为【程序员-小林】提交的休假请求;时长:【2】天。
项目组长已批准角色为【程序员-小林】提交的休假请求;时长:【2】天。
程序员提交了了角色为【程序员-大鹏】提交的休假请求;时长:【3】天。
项目组长提交了了角色为【程序员-大鹏】提交的休假请求;时长:【3】天。
技术经理拦截了角色为【程序员-大鹏】提交的休假请求;时长:【3】天。
技术经理已批准角色为【程序员-大鹏】提交的休假请求;时长:【3】天。
程序员提交了了角色为【程序员-樊胜美】提交的休假请求;时长:【5】天。
项目组长提交了了角色为【程序员-樊胜美】提交的休假请求;时长:【5】天。
技术经理提交了了角色为【程序员-樊胜美】提交的休假请求;时长:【5】天。
部门经理拦截了角色为【程序员-樊胜美】提交的休假请求;时长:【5】天。
部门经理已批准角色为【程序员-樊胜美】提交的休假请求;时长:【5】天。
程序员提交了了角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
项目组长提交了了角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
技术经理提交了了角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
部门经理提交了了角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
首席执行官拦截了角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
首席执行官已批准角色为【程序员-小蚯蚓】提交的休假请求;时长:【10】天。
项目组长提交了了角色为【项目组长-关关】提交的休假请求;时长:【12】天。
技术经理提交了了角色为【项目组长-关关】提交的休假请求;时长:【12】天。
部门经理提交了了角色为【项目组长-关关】提交的休假请求;时长:【12】天。
首席执行官拦截了角色为【项目组长-关关】提交的休假请求;时长:【12】天。
首席执行官已批准角色为【项目组长-关关】提交的休假请求;时长:【12】天。
项目组长提交了了角色为【项目组长-应勤】提交的休假请求;时长:【14】天。
技术经理提交了了角色为【项目组长-应勤】提交的休假请求;时长:【14】天。
部门经理提交了了角色为【项目组长-应勤】提交的休假请求;时长:【14】天。
首席执行官拦截了角色为【项目组长-应勤】提交的休假请求;时长:【14】天。
首席执行官已拒绝角色为【项目组长-应勤】提交的休假请求;时长:【14】天。
技术经理提交了了角色为【技术经理-张璇】提交的休假请求;时长:【20】天。
部门经理提交了了角色为【技术经理-张璇】提交的休假请求;时长:【20】天。
首席执行官拦截了角色为【技术经理-张璇】提交的休假请求;时长:【20】天。
首席执行官已批准角色为【技术经理-张璇】提交的休假请求;时长:【20】天。
部门经理提交了了角色为【部门经理-包奕凡】提交的休假请求;时长:【60】天。
首席执行官拦截了角色为【部门经理-包奕凡】提交的休假请求;时长:【60】天。
首席执行官已批准角色为【部门经理-包奕凡】提交的休假请求;时长:【60】天。
首席执行官提交了了角色为【首席执行官-曲筱绡】提交的休假请求;时长:【120】天。
Master拦截了角色为【首席执行官-曲筱绡】提交的休假请求;时长:【120】天。
Master拒绝了角色为【首席执行官-曲筱绡】提交的休假请求;时长:【120】天。
代码设计
既然知晓了使用责任链解决问题,那么按照责任链的思想解决现实问题。抽象休假处理的接口,Java中接口定义规则。如下图,IHandler接口声明了三个方法,dispatch开头的方法是提交请假申请的方法;onIntercept开头的方法表示是否拦截处理这个休假申请;handle开头的方法表示处理这个请求,可以批准和拒绝该申请,自此休假申请不在向上级提交。
package me.ziuo.design_pattern.cop.employee;
/**
*
* @author ziyuo
* @Description 抽象出来的休假申请处理接口
*/
public interface IHandler {
/**
* 分发请假请求
* @param askModel
* @return 分发结果
*/
boolean dispatchAsk(LeaveAskModel askModel);
/**
* 拦截请假请求
* @param askModel
* @return 拦截与否
*/
boolean onInterceptAsk(LeaveAskModel askModel);
/**
* 处理请假请求
* @param askModel
* @return 是否批准
*/
boolean handleAsk(LeaveAskModel askModel);
}
LeaveAskModel 休假请求类,该数据模型里面保存了修改的天数和申请人信息。如图所示。
package me.ziuo.design_pattern.cop.employee;
/**
*
* @author ziyuo
* @Description 代表请假申请信息对象
*/
public class LeaveAskModel {
private int days;// 休假天数
private Employee askEmp;// 申请人
public LeaveAskModel(int days, Employee askEmp) {
super();
this.days = days;
this.askEmp = askEmp;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public Employee getAskEmp() {
return askEmp;
}
public void setAskEmp(Employee askEmp) {
this.askEmp = askEmp;
}
}
员工的抽象类实现IHandler里面的定义的接口的相关的方法,里面包含了员工角色信息、员工名称信息、可处理的最大申请天数、直属领导(当无权限处理休假申请的时候可以向上提交)。
其中实现IHandler的方法介绍
dispatch开头的方法的具体含义是首先判断本角色是否拦截这个请求,如果拦截则调用请求的方法,是否审批通过取决于handleAsk的结果。
onIntercept开头方法的具体含义,是当角色类型是否不同于提交的对象时,如果提交者申请的提交时长可以处理的情况下,选择拦截该请求后请求响应的审批处理。
handle开头的方法用于进行审批请求,逻辑大概是这样:根据员工的信息来进行响应的处理,这里只是一个实现案例。
如图所示。
package me.ziuo.design_pattern.cop.employee;
public abstract class Employee implements IHandler{
private String actor;//员工角色名称
private String name;//员工名称
private int maxHandleDay;//最大可审批的请假天数(不包含最大天数)
private Employee leader;//直属领导
public Employee(String actor, int maxHandleDay) {
super();
this.actor = actor;
this.maxHandleDay = maxHandleDay;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public int getMaxHandleDay() {
return maxHandleDay;
}
public void setMaxHandleDay(int maxHandleDay) {
this.maxHandleDay = maxHandleDay;
}
public boolean dispatchAsk(LeaveAskModel askModel) {
if(onInterceptAsk(askModel)){
return handleAsk(askModel);
}else{
if(getLeader()!=null){
System.out.println(getActor()+"提交了了角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
return getLeader().dispatchAsk(askModel);
}else{
System.out.println(getActor()+"提交了了角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
//写这段代码的程序员进行审批 ,终于站在了食物链的顶端(原来是在做梦)。
System.out.println("【代码作者】"+"拦截了角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
System.out.println("【代码作者】"+"拒绝了角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
return false;
}
}
}
public boolean onInterceptAsk(LeaveAskModel askModel) {
if(askModel!=null&&!this.getClass().equals(askModel.getAskEmp().getClass())&&askModel.getDays()<this.getMaxHandleDay()){
System.out.println(getActor()+"拦截了角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
return true;
}
return false;
}
public boolean handleAsk(LeaveAskModel askModel) {
if(askModel==null||askModel.getAskEmp().getName().equals("小帥哥0")){
System.out.println(getActor()+"已拒绝角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
return false;
}
if(askModel!=null&&askModel.getDays()<this.getMaxHandleDay()){
System.out.println(getActor()+"已批准角色为【"+askModel.getAskEmp().actor+"】提交的休假请求;时长:【"+askModel.getDays()+"】天。");
return true;
}
return false;
}
public Employee getLeader() {
return leader;
}
public void setLeader(Employee leader) {
this.leader = leader;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
各个对象的封装案例,下面列出来。
程序员类代码
package me.ziuo.design_pattern.cop.employee;
public class Programmer extends Employee {
public Programmer() {
this("程序员", 0);
}
public Programmer(String actor, int maxHandleDay) {
super("程序员", 0);
setLeader(new TeamLeader());
}
public boolean dispatchAsk(LeaveAskModel askModel) {
return super.dispatchAsk(askModel);
}
public boolean onInterceptAsk(LeaveAskModel askModel) {
return !(askModel.getAskEmp() instanceof Programmer);
}
}
TeamLeader类代码
package me.ziuo.design_pattern.cop.employee;
public class TeamLeader extends Employee {
public TeamLeader() {
this("项目组长", 3);
}
public TeamLeader(String actor, int maxHandleDay) {
super("项目组长", 3);
setLeader(new CTO());
}
}
CTO类代码
package me.ziuo.design_pattern.cop.employee;
public class CTO extends Employee {
public CTO() {
this("技术经理", 5);
}
public CTO(String actor, int maxHandleDay) {
super("技术经理", 5);
setLeader(new PV());
}
}
PV类代码
package me.ziuo.design_pattern.cop.employee;
public class PV extends Employee {
public PV(){
this("部门经理", 10);
}
public PV(String actor, int maxHandleDay) {
super("部门经理", 10);
setLeader(new CEO());
}
}
CEO类代码
package me.ziuo.design_pattern.cop.employee;
public class CEO extends Employee {
public CEO(){
this("首席执行官", 120);
}
public CEO(String actor, int maxHandleDay) {
super("首席执行官", 120);
setLeader(null);
}
}
各个角色的向上传递休假申请的处理链,就组成了所谓的责任链;除了顶层的CEO其余的角色都持有他的直接领导。这条持有直接领导的链条就组成了一个单向链表,而责任链模式的部分场景中会直接使用单向的链表来链接责任处理集合。
责任链设计模到此就介绍完毕了。
如果希望了解Android事件分发流程对责任链设计模式的运用请移步下面的链接。
Android事件分发流程(二)源码解析
网友评论