Android事件分发流程(一)责任链设计模式

责任链设计模式

简单介绍

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事件分发流程(二)源码解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容