设计模式-职责链/责任链模式(Chain of Responsibility)分析理解和在Android的应用

介绍

我一直感觉Android源码不能只简单的看,结合设计模式的思想再去看源码会有更深的理解。但是源码中应用某个设计模式,会根据实际情况会有一些差异。所以需要先透彻地理解设计模式。熟悉设计模式的应用场景就可以在Android源码的场景中找到相应的实现,熟悉设计模式就能够透过浩瀚的源码看到本质。
今天突然想到有关View点击事件的分发,感觉它的设计需求就很符合职责链模式(Chain of Responsibility)的应用场景。《Android源码设计模式解析与实战》中也说到了这点。所以本篇先重点介绍职责链模式。
如果已经理解该模式可以直接看下篇:
Android开发-分析ViewGroup、View的事件分发机制、结合职责链模式

职责链模式介绍

定义

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

应用场景

想象这样的一个需求场景:在公司里面申请活动费用,申请人填写表格,交给上级审批,如果活动费用数额在上级的处理范围就处理,然后返回通过请求,否则上级再交给他的上级,处理逻辑同样。最后肯定有一个人处理。

针对上面的场景,就是职责链模式的最典型应用场景。

UML图

设计模式-职责链/责任链模式(Chain of Responsibility)分析理解和在Android的应用_第1张图片

  • Handler:定义职责接口,通常在内部定义处理请求的方法,可以在这里实现后继链。
  • ConcreteHandler:实际的职责类,在这里个类里面,实现在它职责范围内的请求处理,如果不处理,就继续转发请求给后继者。
  • Client:客户端,组装职责链,向链上的具体对象提交请求。

图中最关键的点就是:那条从Handler出发又指向自己的线,它就是实现链式调用的关键。

代码

先上图是为了能够先看清职责链模式的大概框架。现在用实际代码和运行结果展示该模式的实现思想。

Handler抽象类

Handler抽象类作为职责接口,是所有职责对象的父类。

/**
 * Created by LiCola on  2016/05/31  23:16
 */

public abstract class Handler {
    /**
     * 内部持有后继的职责对象
     */
    protected Handler successor;

    /**
     * 注入后继职责对象
     * @param successor
     */
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    /**
     * 抽象父类统一请求处理方法
     * 可以在方法中传值 作为判断条件 也可以不传值利用其它外部条件
     */
    public abstract void handlerRequest(int cost);
}

具体的职责实现对象

为了简洁起见,只实现两个具体的职责对象,在方法内部都提供结果打印显示处理流程。

职责对象1

/**
 * Created by LiCola on  2016/05/31  23:19
 */

public class ConcreteHandler1 extends Handler {

    @Override
    public void handlerRequest(int cost) {
         /*
          根据某个条件判断是否是自己的处理职责范围
          一般是传入的值 或者外部数据
         */

        if (cost<1000){
            //如果是自己的职责范围 就在内部处理
            System.out.println(this.getClass().getSimpleName()+" handler event");
        }else {
            //如果不在自己的职责范围 就判断是否有后继者
            if (this.successor!=null){
                //存在后继者 讲请求传递
                System.out.println(this.getClass().getSimpleName()+" post handler");
                this.successor.handlerRequest(cost);
            }
        }
    }
}

职责对象2

/**
 * Created by LiCola on  2016/05/31  23:19
 */

public class ConcreteHandler2 extends Handler {

    @Override
    public void handlerRequest(int cost) {
        //简化流程 该对象一定能处理事件 不再下发
        System.out.println(this.getClass().getSimpleName()+" handler event");
    }
}

Client客户端

在这里客户端不是简单的调用,它还需要组装职责链,并发起请求

/**
 * Created by LiCola on  2016/05/31  23:27
 */

public class Client {

    public static void main(String[] args){
        //客户端需要自己组装职责链
        //构造职责对象
        Handler handler1=new ConcreteHandler1();
        Handler handler2=new ConcreteHandler2();
        //组装链
        handler1.setSuccessor(handler2);

        //发起请求
        handler1.handlerRequest(2000);

    }
}

分析

运行上面的代码,先看打印出来的结果:
打印结果

说明:
因为发出的请求不在ConcreteHandler1的处理范围,它将事件下发了,而ConcreteHandler2具有最高的权限范围,一定能处理请求。

上面的代码只是最简单的实现,只拼接了两个职责对象,根据实际情况可以有很多的职责对象。

再看一下职责链的调用过程加深印象:

Created with Raphaël 2.1.0 客户端 客户端 职责对象1 职责对象1 职责对象2 职责对象2 发出请求 不在职责范围,事件下发 发出请求 能够处理回应事件 回应请求

模式讲解

看完上面的代码,相信你就能够理解这句话了:

客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。

如果对上面的代码修改成:每个请求都不拦截,而是一步步的往下传,每一步都会处理请求对象,就可以变形成处理请求功能链。

这里提一个概念

隐式接受者:对于请求者而言,它并不知道最终的接受者是谁,但是一般情况下总会有一个对象来处理,因此称为隐式接受者

职责链的构造

刚才的代码只是示例,在客户端只简单的拼接了链。实际使用中,职责链的构造是非常重要的点。那如果构造职责链?

按照实现的角度来分:

  • 客户端组装职责链,在使用时动态的组合链,称为外部链,我们上面的代码就是实现了外部链。
  • Handler中实现链组合,可以称为内部链
  • 还可以在各个职责对象中,由对象自行决定后续处理的对象。这种方式要求每个职责对象处理进行任务处理外,还需要了解整个业务流程。

按照构造链的数据来源分,也就是决定了按照什么顺序呢来组合链的数据可以分为:

  • 在程序中动态组合
  • 通过外部,比如数据库来获取组合数据,这属于数据库驱动方式
  • 通过流程配置文件

如果是从外部获取数据来构建链,那么在程序运行时,会读取这些数据,然后根据数据的要求来获取相应的对象组合起来。

作为一个Android开发者,看到这里是不是感觉外部数据、配置文件、隐式接受者。会不会是App项目中的manifest文件中每个组件的 ,和我们平常的隐式启动Activity
我也只是猜想,没有经过具体的验证证明。附上Intent 和 Intent 过滤器,大家自己去看。

职责链模式的思考

说了这么多,下面总结一下职责链模式的优缺点

优点

  • 请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
  • 动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。

职责链模式的最主要功能就是:动态组合,请求者和接受者解耦

缺点

  • 产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
  • 不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性

Android隐式Intent的隐患

说到职责链模式的缺点,突然想到Android开发中隐式Intent的隐患。
如果我们在代码中,想通过隐式启动处理某个用户请求,比如这样的代码

 Uri uri = Uri.parse(link);
 Intent intent= new Intent(Intent.ACTION_VIEW, uri);
  startActivity(intent);

可能的结果:当没有任何应用处理发送到 startActivity() 的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。

所以要验证 Activity 是否会接收 Intent,请对 Intent 对象调用 resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用 startActivity()。如果结果为空,则不应使用该 Intent。
对应的代码是这样的

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

应用场景

  1. 如果有多个对象可以处理同一个请求,但是具体由哪个对象处理是由运行时刻动态决定的,这种对象就可以使用职责链模式,把处理请求的对象实现成职责对象,然后构造链,当请求在这个链中传递的时候,会根据运行状态判断。
  2. 当不明确接受者的情况下。
  3. 当想要动态指定处理一个请求的对象集合

只所以把第一项说得这么细,就是因为它刚好就是我们Android开发中View的事件分发处理。手在屏幕上点击产生事件,每个View都是职责对象都可以处理事件。具体内容我会在下一篇博客详细分析。
下篇点这里: Android开发-分析ViewGroup、View的事件分发机制、结合职责链模式

总结

  1. 本文首先提出职责链模式概念,并用UML图和模板代码说明模式
  2. 模式讲解指出关键点
  3. 思考说明职责链模式的优缺点,引出Android隐式Intent的隐患并提出解决方案。最后指出应用场景。

你可能感兴趣的:(源码分析,设计模式)