责任链模式:Chain of Responsibility

2,639 阅读8分钟

简介

职责链是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推,直到有对象处理它为止(也可能没有),如此使得每个接收者都有机会接收和处理请求。

对于调用者来说,调用者只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以责任链也将请求的发送者和处理者进行了解耦。

场景举例

一个事件需要经过多个对象处理是一个挺常见的场景,譬如客服咨询流程,采购审批流程,请假流程,软件开发中的异常处理流程,过滤系统等各种各样的流程,可以考虑使用责任链模式来实现。

如图,假如你刚购置了一个新的硬件设备,但是连接上电脑并不能正确执行,于是你决定拨打技术支持电话。

首先你会听到自动回复器的机器合成语音,它提供了针对各种问题的九个常用解决方案,你按照提示操作,机器人将你转接到一位人工客服人员。

客服发现问题的技术性比较强,无法提供任何具体的解决方案。于是他将你转接到一位技术工程师。

最后,工程师告诉了你新硬件设备驱动程序的下载网址,以及操作步骤,你按照说明解决了问题,几经周折,终于大功告成。

概念

  1. 处理者 Handler:声明了所有具体处理者的通用接口。该接口通常包含用于请求处理的方法,以及设置下一个处理者的方法。
  2. 基础处理者(Base Handler):通常情况下,该类中定义了一个保存对于下个处理者引用的成员变量。客户端可通过将处理者传递给下个处理者的构造函数或设定方法来创建链。该类还可以实现默认的处理行为:确定下个处理者存在后再将请求传递给它。
  3. 具体处理者(Concrete Handlers):包含处理请求的实际代码。每个处理者接收到请求后,都必须决定是否进行处理,以及是否沿着链传递请求。处理者通常是独立且不可变的,需要通过构造函数一次性地获得所有必要地数据。
  4. 客户端(Client)可根据程序逻辑一次性或者动态地生成链。值得注意的是,请求可发送给链上的任意一个处理者,而非必须是第一个处理者。

活学活用

以请假流程为例,一般公司普通员工的请假流程简化如下

普通员工发起一个请假申请,当请假天数小于3天时主管即可审批;当请假天数大于3天时,需要提交给项目经理审批,但若请假天数大于7天,就要提交给总经理审批。


public interface Handler {
    /**
     * 处理请假
     *
     * @param days 请假天数
     * @return 是否批准
     */
    boolean handleHoliday(int days);

    /**
     * 设置责任链的下一个处理者
     *
     * @param h 下一个处理者
     */
    void setNext(Handler h);
}

public class BaseHandler implements Handler {
    private Handler next;

    @Override
    public boolean handleHoliday(int days) {
        if (next != null) {
            return next.handleHoliday(days);
        } else {
            System.out.println("未批准");
            return false;
        }
    }

    @Override
    public void setNext(Handler h) {
        next = h;
    }
}

public class LeaderManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        if (days <= 3) {
            System.out.println("LeaderManager 批准");
            return true;
        } else {
            return super.handleHoliday(days);
        }
    }
}

public class ProjectManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        if (days <= 7) {
            System.out.println("ProjectManager 批准");
            return true;
        } else {
            return super.handleHoliday(days);
        }
    }
}

public class DeptManager extends BaseHandler {
    @Override
    public boolean handleHoliday(int days) {
        System.out.println("DeptManager 批准");
        return true;
    }
}

public class Client {
    public static void main(String[] args) {
        Handler leaderManager = new LeaderManager();
        Handler projectManager = new ProjectManager();
        Handler deptManager = new DeptManager();
        leaderManager.setNext(projectManager);
        projectManager.setNext(deptManager);
        leaderManager.handleHoliday(5);
    }
}

// Logcat:
// ProjectManager 批准

真实例子

Android 事件传递机制

  • 伪代码
public boolean dispatchTouchEvent(MotionEvent event){
    boolean consume = false;
    if (onInterceptTouchEvent(event)) {
        consume = onTouchEvent(event);
    } else {
        consume = child.dispatchTouchEvent(event);
    }
    return consume;
}

使用场景

  • 当必须按顺序执行多个处理者时,可以使用该模式。
    • 无论你以何种顺序将处理者连接成一条链,所有请求都会严格按照顺序通过链上的处理者。
  • 如果所需处理者及其顺序需要在运行时进行改变,也可以使用职责链模式。
    • 调用者可以根据运行时环境,动态地插入和移除处理者,或者改变其顺序。

纯的与不纯的责任链模式

“一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又把责任向下传的情况。

在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。

纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的。但是在实际的系统里,纯的责任链很难找到。如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。 ”

优缺点

  • 优点
    • 可控的请求处理的顺序。
    • 单一职责原则:每个处理者职责单一,并且对发起操作和执行操作的类进行了解耦。
    • 开闭原则:你可以在不更改现有代码的情况下在程序中新增处理者。
  • 缺点
    • 有可能出现请求不被任何处理者处理的情况;
    • 责任链建立不当,有可能出现死循环;

和其他设计模式比较

  • 常见组合
    • 责任链通常和组合模式结合使用。在这种情况下,父组件接收到请求后,可以将请求沿包含子组件的链一直传递至对象树的叶结点。 Android 中 View 及其时间传递机制正是如此;
  • 容易混淆
    • 责任链模式与命令模式
      • 责任链按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理。
      • 命令模式在发送者和请求者之间建立单向连接。
    • 责任链和装饰模式
      • 装饰模式主要目的是给原对象增加新行为,责任链的主要目的是让多个处理者都有机会处理某个请求;
      • 责任链的管理者可以相互独立地执行一切操作,还可以随时停止传递请求;
      • 可以在遵循基本接口的情况下扩展对象的行为;
      • 装饰无法中断请求的传递。

项目中例子

  • Router?

  • OkHttp Interceptor

    • 在OkHttp的内部实现中,interceptors并不仅仅是拦截器这么简单。实际上,OkHttp发送网络请求的一切核心功能,包括建立连接、发送请求、读取缓存等,都是通过interceptors来实现的,这些interceptors在运行的时候彼此协作,构成了一个interceptor chain。
/**
* 项目中自定义的 Interceptor
*/
object DomainSwitchInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        val requestDomain = request.url().host()
        val domainManager = DomainManagerFactory.getInstance().getDomainManagerByDomain(requestDomain)
        val currentDomain = domainManager?.pickHostWithoutSchema()
        if (currentDomain != null && requestDomain != currentDomain) {
            request = request.newBuilder()
                .url(request.url().newBuilder().host(currentDomain).build())
                .build()
        }
        return chain.proceed(request)
    }
}


/**
* 将自定义 Interceptor 添加到 OkHttp
* 最终添加到 OkHttpClient 静态内部类 Builder 的 final List<Interceptor> interceptors 中
*/
object RetrofitRestClient {

    val defaultOkHttpClient: OkHttpClient by lazy {
        VolleyManager.getOkHttpClient()
            .newBuilder()
            .addInterceptor(RequestCommonParamsInterceptor)
            .addInterceptor(DomainSwitchInterceptor)
            .addInterceptor(RequestMonitorInterceptor)
            .dns(HttpDns)
            .build()
    }
}

/**
* 所有请求都是从 OkHttp 的 RealCall 的 getResponseWithInterceptorChain() 方法开始
*/
Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors()); // client.interceptors()包含了自定义的拦截器
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 将所有 Interceptor 放入 RealInterceptorChain 构造责任链
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }
  
  /**
 * OkHttp Interceptor
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  /**
  * 构建责任链的接口
  */
  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

/**
* Chain 的实现类 RealInterceptorChain 的 proceed 方法
*/
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

Article by Panxc