【Android设计模式】从Okhttp看懂责任链模式

2019-10-27  本文已影响0人  Android小Y
会写代码是门槛,写好代码是本事

 

前言

平时写代码的时候可能为了完成某一个任务而只是应付性地编码,然后写完理直气壮地来一句"又不是不能用!",但如果要把编码当作一项艺术来打造,那就需要结合我们的设计模式了。设计模式可以运用在诸多方面,让我们的代码更加优雅。设计模式在Android中也是无处不在,动态代理、建造者、工厂、适配器....等等等等,可见它对我们的重要性。最近结合责任链模式对项目中的启动弹窗处理进行了重构。

 

什么是责任链模式?

责任链模式就是一个请求发送到接收者,接收者组成链式结构,沿着链式结构传递请求,直到有对象处理请求。

比如在一个场景可能有ABCD四种情况的处理,将它们组成一条链式结构,我把请求发送给A,A接收到这个请求后,判断是否处理,如果不处理就直接传递给B,同样B如果不处理就传递给C,直至传递到D。这种一级一级的传递就是链式结构。传递过程中也可以直接处理结束链式循环,比如处理到B时,处理掉请求,不会继续传递到C和D。

如何实现责任链模式?

最近项目中刚好有这么一个需求,在启动应用首页时,会有多个满足该场景的弹窗需要弹出,比如隐私协议弹窗、强制更新弹窗、用户引导弹窗、通知权限开启弹窗;每个弹窗都是要满足一定的条件才会弹出,如果不控制好它们之间的逻辑,可能会出现同时弹出两个弹窗的情况,从用户体验来看肯定是不合适的。因此需要让它们一一判断是否满足条件,最终只展示一个。
如果不使用设计模式,可能处理方式就是通过嵌套的if判断去逐个判断是否满足条件,也能实现效果,伪代码如下:

public void checkShowDialog(){
        if(强制更新弹窗满足条件){
            //弹出强制更新
        }else{
            if(隐私协议弹窗满足条件){
                //弹出隐私协议
            }else{
                if(用户引导弹窗满足条件){
                    //弹出用户引导
                }else{
                    if(满足通知权限弹窗条件){
                        //弹出通知权限
                    }
                }
            }
        }
}

但一方面代码结构看起来不清晰,一方面如果要控制它们弹出的优先级,又要调整很多判断代码。这时候责任链模式就可以派上用场了。
 
1. 定义一个责任链处理者抽象类,也是本模式最关键的地方:

public abstract class AbstractCheck {
    
    protected AbstractCheck nextCheck;

    //关联下一个处理者
    public void setNextCheck(AbstractCheck next) {
        this.nextCheck = next;
    }

    //检查该弹窗展示的条件是否满足
    abstract void checkShow(ICheckCallback callback);
}

其实就是持有下一个处理者的实例,然后当自己不满足条件时,可以调用 nextCheckcheckShow方法,即通知下一个处理者,我不处理,轮到你处理请求了。
接着就可以将每个弹窗的判断条件都单独定义成一个继承于AbstractCheck的子类,例如更新弹窗的写法会变成这样:

/**
 * 更新弹窗职责检测类
 */
public class UpgradeDialogCheck extends AbstractCheck{

    @Override
    void checkShow() {
        reqHasNewVersion(new RequestCallback<Boolean>(){
            @Override
            public void onSuccess(Boolean param) {
                if(param){
                    showUpgradeDialog();
                }else{
                    if(nextCheck != null){
                        nextCheck.checkShow();
                    }
                }
            }

            @Override
            public void onFailed(int code) {
                if(nextCheck != null){
                    nextCheck.checkShow();
                }
            }
        });
    }
    
    public void reqHasNewVersion(RequestCallback<Boolean> callback){
        //请求更新接口
    }
    
    public void showUpgradeDialog(){
        //展示更新弹窗
    }
}

可以看到,在重写的父类的checkShow方法里面,先是发起了网络请求,获取得知是否需要更新,如果需要,就展示更新弹窗,如果不需要,就将这个展示权通过 nextCheck.checkShow(callback) 丢给它的下一个处理者去决定。其他的弹窗也是依照这样的写法去定义,就不一一列举了,虽然代码量变多了,但是每个弹窗的逻辑都在它们各自的检测类里面去处理,代码结构也变得清晰很多。

3. 定义好了每个弹窗的职责类之后,就差最后一步,关联它们的关系,将它们串成一条链:

public class DialogManager {
    
    private AbstractCheck upgradeCheck;

    public DialogManager() {
        upgradeCheck = new UpgradeDialogCheck();
        AbstractCheck privacyCheck = new PrivacyDialogCheck();
        AbstractCheck guideCheck = new GuideDialogCheck();
        AbstractCheck notifyCheck = new NotifyDialogCheck();
        upgradeCheck.nextCheck = privacyCheck;
        privacyCheck.nextCheck = guideCheck;
        guideCheck.nextCheck = notifyCheck;
    }

    /**
     * 调用第一个处理者进行处理,触发整条链
     */
    public void startCheck(){
        upgradeCheck.checkShow();
    }
}

可以看到,两两之间都通过nextCheck关联起来,谁先处理谁后处理,优先级分明,通过调用 startCheck触发第一个处理者处理,后面就会自动按着职责链的顺序走下去。责任链模式中用于处理请求的每个对象,并不需要关心该请求的上一个或下一个处理者,只需要将当前处理的进度,组织成请求处理者遵循的接口规范即可,并且确保请求处理者的响应,也遵循统一的接口规范,这样就能使请求处理链路,在某一时刻一旦满足客户端需求时,能直接打断,并将响应结果反馈给客户端。 且如果某一天产品要叫你调整它们之间的优先级,就方便很多了~

 

Okhttp源码中的责任链模式

我们都知道,okhttp可以针对请求配置很多拦截器,而这些拦截器正是通过责任链模式链接起来,并最终返回处理的结果,可以从Okhttp的 execute 方法看到:

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

可以看到首先调用的就是 getResponseWithInterceptorChain 返回拦截的结果赋值给Response对象,进一步看 getResponseWithInterceptorChain 的源码:

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(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.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
}

首先创建了一个interceptors容器,接着分别将客户端自定义、请求重试、桥接、缓存等不同职责的拦截器,依次添加到了这个容器中,每个拦截器就相当于责任链上对请求的不同处理者。最后,创建了一个RealInterceptorChain拦截链,并将interceptors容器添加到了这个拦截链中,再通过RealInterceptorChain的proceed方法触发拦截链,使每个拦截器被依次执行。
那我们随便找一个拦截器,看看它是设计的思路,查看CacheInterceptor缓存拦截器的源码如下:

责任链模式
继承于一个Interceptor接口,并实现它的 intercept,且 intercept有一个参数Chain,且调用了chain的process方法: 责任链模式
为何要这么做,先留个悬念,我们看回 RealInterceptorChain 拦截链这个类,看下它对这些拦截器做了什么:
首先,它是实现了Chain接口:
public final class RealInterceptorChain implements Interceptor.Chain {

实现process方法:


责任链模式

可以看到,RealInterceptorChain的process方法中,会生成一个RealInterceptorChain对象,且注意到index+1,即生成下一个Chain对象,并且同时获取拦截器集合里的下一个拦截器,调用它的intercept,将下一个Chain(next)作为参数传给他去处理,回顾到刚才上面说的,拦截器的interpect里面调用了chain的process,也就是说,每一个拦截器都会持有下一个拦截器的chain对象,并通过chain的process方法,触发RealInterceptorChain里的index下标再+1,从而串联起整个拦截链。

 

总结

责任链模式的本质是解耦请求和处理,将节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动了起来。因为责任链模式具备链式结构,当处理者较多时,可以很清晰地排列出来,且后续替换处理者或调整优先级时也减少很多维护成本。
 

GitHubGitHubZJY
CSDN博客IT_ZJYANG
简 书Android小Y

上一篇 下一篇

猜你喜欢

热点阅读