Switch语句就是责任链模式?

377 阅读6分钟

责任链模式(Chain of Responsibility Pattern)

什么是责任链模式

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

责任链模式将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

ac7cbdb8cc51c857f3fc7c78f7542554cd228360.jpg

类结构图:

3440348a166e661960aa813ab79a2e37a02ed227.png

  • Client(客户端):实例化一个处理器的链,在第一个链对象中调用handleRequest 方法。
  • Handle(处理器):抽象类,提供给实际处理器继承然后实现handleRequst方法,处理请求
  • ConcreteHandler(具体处理器):继承了handler的类,同时实现handleRequst方法,负责处理业务逻辑类,不同业务模块有不同的ConcreteHandler。另外还负责转发请求,如果该请求超出了当前处理者类的处理范围,可以将该请求转发给下家

模式的实现

责任链模式非常简单异常好理解,相信我它比单例模式还简单易懂,其应用也几乎无所不在,它最原始的结构——分支语句:

public class SimpleResponsibility {
    public static void main(String[] args) {
        int request = (int) (Math.random() * 3);
        switch (request) {
        case 0:
            System.out.println("Xiaoming handle it: " + request);
            break;
        case 1:
            System.out.println("Aige handle it: " + request);
            break;
        case 2:
            System.out.println("Boni handle it: " + request);
            break;
        default:
            break;
        }
    }
}

我们从Switch分支语句中来看一下,责任链模式的实质。定义中说的请求(相当于代码中的request)沿着一条“链”(代码中的switch语句)传递,当某个处于“链”上的处理者(case定义的条件)符合条件便会处理完成请求。当然我们实际应用中的责任链模式是没有那么简单的。

简单实现

举个栗子,一般大公司采购都会比较麻烦,流程很多,得经过一层层审批。比如主管可以审批5万元以下的采购单,经理审批小于10万的单,CEO审批小于50万的单,再大的金额就得到董事会了。

059fc9b2603c5e2137296153679909cc20214153.jpg

/**
 * 场景模拟类
 */
public class Client {
    public static void main(String[] args) {
        /*
         * 采购员申请30000元的订单
         */
        Buyer buyer = new Buyer(30000);

        /*
         * 再来四个老大
         */
        Leader leader = new TeamLeader();
        Leader manager = new Manager();
        Leader ceo = new CEO();
        Leader board = new Board();

        /*
         * 设置老大的上一个老大
         */
        leader.setLeader(manager);
        manager.setLeader(ceo);
        ceo.setLeader(board);

        // 处理申请
        leader.handleRequest(buyer);
    }
}

所有领导都有同一个处理逻辑(handleRequest),这里定义一个leader抽象类:

/**
 * 领导人抽象类
 */
public abstract class Leader {
    private int expenses;// 当前领导能批复的金额
    private Leader mSuperiorLeader;// 上级领导

    /**
     * @param expenses 当前领导能批复的金额
     */
    public Leader(int expenses) {
        this.expenses = expenses;
    }

    /**
     * 回应采购
     * @param buyer 采购员
     */
    protected abstract void reply(Buyer buyer);

    /**
     * 处理请求
     * @param buyer 具体的程序猿
     */
    public void handleRequest(Buyer buyer) {
        if (buyer.getExpenses() <= expenses) {
            // 那么就由当前领导批复即可
            reply(buyer);
        } else {
            /*
             * 否则看看当前领导有木有上级
             */
            if (null != mSuperiorLeader) {
                // 有的话简单撒直接扔给上级处理即可
                mSuperiorLeader.handleRequest(buyer);
            } else {
                // 没有上级的话就批复不了……不过在这个场景中总会有领导批复的淡定
                System.out.println("Goodbye my money......");
            }
        }
    }

    /**
     * 为当前领导设置一个上级领导
     * @param superiorLeader 上级领导
     */
    public void setLeader(Leader superiorLeader) {
        this.mSuperiorLeader = superiorLeader;
    }
}

经理类实现:

/**
 * 经理类
 */
public class Manager extends Leader {
    public Manager() {
        super(100000);
    }

    @Override
    protected void reply(Buyer buyer) {
        System.out.println(buyer.getApply());
        System.out.println("Manager: Of course Yes!");
    }
}

采购员类实现:

/**
 * 采购员类
 */
public class Buyer {
    private int expenses;// 声明整型成员变量表示采购费用
    private String apply = "领导您好,需要您批复钱采购。";

    public Buyer(int expenses) {
        this.expenses = expenses;
    }

    public int getExpenses() {
        return expenses;
    }

    public String getApply() {
        return apply;
    }
}

上面的示例设计,将请求和处理分离,对于采购员来说,不需要知道是谁给他批复的钱,而对于领导们来说,也不需要确切地知道是批给哪个采购员,只要根据自己的责任做出处理即可,由此将两者解耦。

从OkHttp了解责任链模式

Android网络框架--OkHttp(2013发布1.0版本)。一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,用于替代HttpUrlConnection和Apache HttpClient(API23 6.0里已移除HttpClient)。

它的工作原理简单来说就是,先利用socket建立了与服务器的TCP连接,建立连接之后,在根据具体的需求,将符合HTTP协议的请求报文拼接好,进而通过刚才的连接传递到服务器,然后再读取服务器的响应。

okhttp请求调用:

public class OkHttpDemo {
   final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5,TimeUnit.SECONDS)
            .build();

    public Response request() throws IOException {
        final Request request = new Request.Builder()
                .url("https://www.baidu.com")
                .build();
       return mOkHttpClient.newCall(request).execute();
    }
}
OkHttp请求流程图:

cca63189afbe0e7e44c322947b0013452f3a9ec3.jpg

从请求流程图可以看到,真正发出网络请求,解析返回结果的,是 getResponseWithInterceptorChain()

@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
    val interceptors = mutableListOf<Interceptor>()

    //在配置OkHttpClient时,自定义的拦截器。
    interceptors += client.interceptors
    //负责失败重试以及重定向的拦截器。
    interceptors += RetryAndFollowUpInterceptor(client)
    //负责把用户构造的请求转换为发送到服务器的请求;把服务器返回的响应转换为用户友好的响应的拦截器。
    interceptors += BridgeInterceptor(client.cookieJar)
    //负责读取缓存直接返回、更新缓存的拦截器
    interceptors += CacheInterceptor(client.cache)
    //负责和服务器建立连接的拦截器
    interceptors += ConnectInterceptor
    if (!forWebSocket) {
      //在配置OkHttpClient时,自定义的拦截器。
      interceptors += client.networkInterceptors
    }
    //负责向服务器发送请求数据、从服务器读取响应数据的拦截器
    interceptors += CallServerInterceptor(forWebSocket)

    val chain = RealInterceptorChain(
      call = this,
      interceptors = interceptors,
      index = 0,
      exchange = null,
      request = originalRequest,
      connectTimeoutMillis = client.connectTimeoutMillis,
      readTimeoutMillis = client.readTimeoutMillis,
      writeTimeoutMillis = client.writeTimeoutMillis
    )

    var calledNoMoreExchanges = false
    try {
      // 经过各个拦截器处理后,返回response
      val response = chain.proceed(originalRequest)
      if (isCanceled()) {
        response.closeQuietly()
        throw IOException("Canceled")
      }
      return response
    } catch (e: IOException) {
      calledNoMoreExchanges = true
      throw noMoreExchanges(e) as Throwable
    } finally {
      if (!calledNoMoreExchanges) {
        noMoreExchanges(null)
      }
    }
}

Interceptor(拦截器) 是 OkHttp 最核心的一个东西,不要误以为它只负责拦截请求进行一些额外的处理(例如 cookie),实际上它把实际的网络请求、缓存、透明压缩等功能都统一了起来,每一个功能都只是一个 Interceptor,它们再连接成一个 Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。

从 getResponseWithInterceptorChain 函数我们可以看到,Interceptor.Chain 的分布依次是:

okhttpinterceptors

  1. 在配置 OkHttpClient 时设置的 interceptors
  2. 负责失败重试以及重定向的 RetryAndFollowUpInterceptor
  3. 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor
  4. 负责读取缓存直接返回、更新缓存的 CacheInterceptor
  5. 负责和服务器建立连接的 ConnectInterceptor
  6. 配置 OkHttpClient 时设置的 networkInterceptors
  7. 负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor

在这里,位置决定了功能,最后一个 Interceptor 一定是负责和服务器实际通讯的。重定向、缓存等一定是在实际通讯之前的。

责任链模式在这个拦截器链条 中得到了很好的实践。对于把 Request 变成 Response 这件事来说,每个拦截器都可能完成这件事,所以循着链条让每个拦截器自行决定能否完成任务以及怎么完成任务(自力更生或者交给下一个拦截器)。

建立连接的ConnectInterceptor:
object ConnectInterceptor : Interceptor {
  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val exchange = realChain.call.initExchange(chain)
    val connectedChain = realChain.copy(exchange)
    return connectedChain.proceed(realChain.request)
  }
}
拦截器链实体RealInterceptorChain:
class RealInterceptorChain(
  internal val call: RealCall,
  private val interceptors: List<Interceptor>,
  private val index: Int,
  internal val exchange: Exchange?,
  internal val request: Request,
  internal val connectTimeoutMillis: Int,
  internal val readTimeoutMillis: Int,
  internal val writeTimeoutMillis: Int
) : Interceptor.Chain {
   ...//省略部分代码

  @Throws(IOException::class)
  override fun proceed(request: Request): Response {
    ...//省略部分代码

    // Call the next interceptor in the chain. 
    // 复制一个index+1的RealInterceptorChain.
    val next:Chain = copy(index = index + 1, request = request)
    val interceptor = interceptors[index]

    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    ...//省略部分代码
    return response
  }
}

其实 Interceptor 的设计也是一种分层的思想,每个 Interceptor 就是一层。为什么要套这么多层呢?分层简化了每一层的逻辑,每层只需要关注自己的责任(单一原则),而各层之间通过约定的接口/协议进行合作(面向接口编程),共同完成复杂的任务。

简单应该是我们的终极追求之一,尽管有时为了达成目标不得不复杂,但如果有另一种更简单的方式,我想应该没有人不愿意替换。

总结

责任链的优点:

  • 单一职责:⼀个类和⽅法只做⼀件事 。降低了耦合度,调用方不需要知道请求由哪些 handler 处理,而 handler 也不需要知道互相之间的传递关系,由系统组织和分配。
  • 开闭原则:一个软件实体(如类、模块和函数)应该对扩展开放,对修改关闭。带来良好的扩展性,使得增加处理者的实现很简单,可以在链路中任意位置插入新的 handler。

责任链的缺点:

  • 链路长:请求从链头发出,直到被中断或者结束,可能会影响系统性能。
  • 排查较慢:一般是不太能一目了然的看到是哪个节点出现了问题,需要多添加一些核心日志。

参考

Android设计模式源码解析之责任链模式

拆轮子系列:拆 OkHttp - Piasy的博客 | Piasy Blog

责任链模式在转转精准估价中的应用 - 掘金