Android设计模式—责任链模式

1.责任链模式

责任链模式是指将一个请求从链式的首端发出,沿着链的路径依次传递给每个节点对象,直到有对象处理这个请求为止,使多个对象都有机会处理请求,从而避免了请求发送者与接收者之间的耦合关系,使编程更有灵活性。

责任链模式是一种对象的行为模式。

责任链模式允许开发者通过处理者链进行顺序发送,每个链节点在收到请求后,具备两种能力:对其进行处理(消费)或将其传递给链的下个处理者。当你想要让一个以上的对象有机会能处理某个请求时,就可以使用责任链模式。通过责任链模式,为某个请求创建一个对象链,每个对象链依序检查此请求,并对其进行处理,或者将它传给链中的下一个对象。

871569acd5cd460caaa9bdc3a8211495.webp

责任链上每个节点的产物是不同的(当然也可以相同,但相同的话就没必要通过责任链去处理了,可能放在单个对象中会更合适),像链表结构一样,每个节点除了需要包含指向下一个链节点的索引以及必要时终止传递的能力外,还需要具备传递给下一个节点产物的能力。

责任链模式的优势:

①降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。

②增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。

③增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。

④责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

⑤责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

责任链模式的缺点:

①不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。

②对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。

③职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

看一下责任链模式的 UML 图:

8d342e05692349c99b5d1101d2943dd9.webp

从UML图中可以看出责任链里一般包含4种角色:

①Client 客户端,定义链的规则和运行方式,根据具体业务场景动态生成链(所以链并不是固定不变的,可定制组合,并选择链头和链尾)

②Handler 处理者接口,主要用于声明具体处理者的通用能力,一般包含抽象处理能力以及指向下一个节点的能力

③BaseHandler 基本处理者,这是一个可有可无的角色,可以根据业务场景将具体处理者中的一些共有逻辑放到该类当中

④ConcreteHandlers 具体处理者,构成了链中的处理节点,核心职能是处理请求,决定请求是在该节点消费掉还是沿着链继续传递(具体处理者之间独立且不可变)

可以看出,责任链模式核心的逻辑是处理和传递,同时具备由外部灵活定制的能力。

通过 UML 图也可以看出责任链固定的几步实现方式:

①声明Handler接口定义节点处理的接口

②通过创建抽象处理者基类消除具体处理者之间的重复模版代码

③依次创建具体处理者子类及其实现方法,通过具体处理类决定当前处理类是否要消费这个请求或者沿着链继续传递

④最终体现到业务层,由Client对象自行组装实现的链节点,实现逻辑处理和调用对象的解耦

适用场景:

①有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。

②在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

③可动态指定一组对象处理请求。

其实只要涉及到逻辑顺序处理的,都可以使用责任链模式来处理。但从实际场景出发,决定是否使用该模式要考虑一下两个因素:

①场景是不是够复杂,逻辑链是不是很长

②是否有灵活变化的业务变化场景需求

同时还要注意使用责任链不可避免带来的三个问题:

①处理者的数量问题。对链中请求处理者的遍历,如果处理者太多遍历必定会影响性能,特别是在一些递归调用中,所以要慎重

②代码出现问题时,不容易观察运行时的特征,有碍于排查问题

③需要cover请求即使传递到链尾端也一直没被处理,从而导致的一些异常问题

2.源码中用到的责任链模式

OkHttp框架中拦截器用到了责任链模式。

Android事件分发机制是责任链模式最典型的应用:dispatchTouchEvent就是责任链中将事件交给下一级处理的,onInterceptTouchEvent就是责任链中自己处理事务的。onTouchEvent 是责任链中事件上报的事件链。

在屏幕上点击一次的行为在 Android 源码中的传递路径如下:

d10b5f0c349c4a32aef7ae0d1060f2ce.webp

可以看到,Android 系统的事件传递和分发也是通过链的方式来实现的。如果将 Activity、ViewGroup、View 三者作为具体处理者,通过它们自身的 dispatchTouchEvent() 方法对事件进行消费和传递,那就是一个标准的责任链模式。

public boolean dispatchTouchEvent(MotionEvent ev) {

    if(onInterceptTouchEvent(ev)) {

            // onInterceptTouchEvent 方法作为是否需要在本处理者中被消费的判断,如果为 true,则在本控件中消费

            this.onTouchEvent(ev);

    } else {

            // 本控件不被拦截,则传递给下一个控件的 dispatchTouchEvent 方法当中

            next.dispatchTouchEvent(ev);

    }

}

当用户的点击事件传递到控件最顶端的 View 后,如果在该 View 中 touch 事件还没有被消费掉,那么它会依照原来传递过来的链路重新回到调用链最开始的地方,即从 View 的 onTouch() 或者 onTouchEvent() 重新回到 Activity 的 onTouchEvent() 方法中。

这是一个非常经典的责任链模式,如果自己能处理就拦截下来自己干,如果自己不能处理或者不确定就交给责任链中下一个对象。 这种设计使得上层View既可以直接拦截该事件自己处理,也可以先询问(分发给)子View,如果子View需要就交给子View处理,如果子View不需要还能继续交给上层View处理。既保证了事件的有序性,又非常的灵活。

责任链模式有一个需要特别注意的点是,如果请求到链的末端还没有被处理的话极有可能会让代码出现稳定性问题。所以 Android 通过重新将请求交回给最初的链节点方式来解决这个问题,这样做的好处是:

①请求不论走到哪一步都可控(即一定会被处理,即使可能最终是空实现)

②让和 UI 相关的功能类具备一致的行为方式(使 Activity、ViewGroup、View 均具备分发和消费能力)

猜你喜欢

转载自blog.csdn.net/zenmela2011/article/details/126012374