Android进阶之路Android开发经验谈Android技术知识

Android 开发中常用设计模式解读之-责任链(拦截器)

2019-05-08  本文已影响29人  kenan

模式定义与设计解读

设计模式晦涩的定义总是难懂,但同时这些定义又有着独特的涵义。本文想通过最直观的例子,把这些晦涩的定义反应在代码层面上。代码是设计模式最直观的表达,当你看不懂定义时,代码会说话。希望这篇解读可以帮助到你。预计阅读10分钟。

定义

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

注意一下加粗的关键字,解读会重新定义这些难懂的定义。

解读

在生活经常存在一种场景,完成一件事情,需要串行执行几个步骤。比如把大象装冰箱,需要三个步骤。

  1. 把冰箱门打开
  2. 把大象装冰箱
  3. 把冰箱门关上

现在把这件事情抽象成代码,并与上文中的定义对应。

/**
 * 请求上下文
 */
public class RequestContext {
  
  public Elephant mElephant;//大象
  
  public Refrigerator mRefrigerator;//冰箱

  public RequestContext(Elephant elephant, Refrigerator refrigerator) {
    mElephant = elephant;
    mRefrigerator = refrigerator;
  }

  public static class Elephant{
  }

  public static class Refrigerator{
  }
  
}

proceed 英 [prə'siːd]
vi. 开始;继续进行;发生;

/**
 * 请求链
 */
public interface Chain {
  /**
   * 获取请求实体
   */
  RequestContext getContext();

  /**
   * 开始或继续执行请求。
   */
  void proceed(RequestContext context);
  
}
/**
 * 处理者
 */
public interface Handler {

  /**
   * 处理链条上的请求
   */
  void handle(Chain chain);
  
}

  1. 先接收请求(接收)
  2. 处理请求 (处理)
  3. 通知Chain,继续执行请求。(发送)

每个操作步骤只需要关注自己需要如何操作请求,而不需要关心操作完成后下个步骤是什么。只需要通知Chain,我执行完毕了,可以继续执行下一步了。从而避免了发送者和接收者之间的耦合关系。那么如何解耦呢,自然是链条来解决。链条上持有一系列操作步骤,存储成List<Handler>,并且记录当前执行到第几步int index,再结合之前定义的Chain接口,Chain实现类如下

/**
  * 链实现类
  */
public class ProcessChain implements Chain {

  public List<Handler> mProcessors;
  public RequestContext mChainContext;
  public int mIndex;

  public ProcessChain(List<Handler> processors, int index, RequestContext chainContext) {
    mProcessors = processors;
    mIndex = index;
    mChainContext = chainContext;
  }

  @Override
  public RequestContext getContext() {
    return mChainContext;
  }

  @Override
  public void proceed(RequestContext processContext) {
    if (mProcessors.size() > mIndex) {
      //获取当前处理者
      Handler processor = mProcessors.get(mIndex);

      //更新index 与 Context
      ProcessChain nextChain = new ProcessChain(mProcessors, mIndex + 1, processContext);

      //处理者执行处理步骤
      processor.handle(nextChain);
    }
  }
}

proceed方法可能会觉得有些绕,其实道理很简单。着重理解一下nextChain即可,这里是重新构造了一个Chain,其实也可以理解为更新了一下Chain的数据,将index+1,以及更新RequestContext 。接下来讲到Processor的实现类,二者结合起来理解会更容易nextChain

/**
 * 打开冰箱
 */
public class HandlerOpenRefrigerator implements Handler {
  @Override
  public void handle(Chain chain) {
    RequestContext requestContext = chain.getContext();
    //处理具体操作
    if(!requestContext.mRefrigerator.isOpen()) {
      requestContext.mRefrigerator.open();
    }
    //处理完毕 继续执行下个操作
    chain.proceed(requestContext);
  }
}
/**
 * 移动大象
 */
public class MoveElephantHandler implements Handler {
  @Override
  public void handle(Chain chain){
    RequestContext requestContext = chain.getContext();
    //处理具体操作
    if(requestContext.mRefrigerator.isOpen()) {
      requestContext.mElephant.move();
      //处理完毕 继续执行下个操作
      chain.proceed(requestContext);
    }else{
      //发生异常 中断链条
      chain.abort();
    }
  }
}

讲到这里再回头读一下最开始定义的那句话。你会有不一样的理解。如果没有,自己敲一遍代码再试试。

public class MyClass {
  public static void main(String[] args) {
    //构造请求
    RequestContext requestContext = new RequestContext(new RequestContext.Elephant(),new RequestContext.Refrigerator());

    //构造处理步骤
    List<Handler> list = new ArrayList<>();
    
    //你也可以不加打开冰箱试试结果
    list.add(new OpenRefrigeratorHandler());
    list.add(new MoveElephantHandler());
    list.add(new CloseRefrigeratorHandler());
    
    //执行任务
    ProcessChain processChain = new ProcessChain(list,0,requestContext);
    processChain.proceed(requestContext);
  }
}

Demo地址

思想理解与实践运用

理解

责任链重点是在解耦,如何将一个任务的不同步骤之间没有耦合关系,每个步骤专注负责自身,而不需要关心其他步骤,以及其他步骤的变化,这些变化可以交给更高层次来控制,而不是在任务节点上控制。比如上述例子中,如果装大象上线后发现很容易失败,需要先把大象切开(一个残忍的举例),那么只需要在控制层添加list.add(new CutElephantHandler)即可。这种修改对于既有的代码侵略性极低,因为步骤之间解耦很彻底,并且这种可插拔式的代码,组合性极强。使得代码的扩展性和可维护性极高。

运用

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;
    
  }
}

是不是很熟悉。Okhttp中拦截器就是责任链模式运用。我们在进行应用开发的时候都会在请求中增加一些通用信息,比如在 header 中增加用户的token信息等等以及在请求的过程中修改请求的 requestresponse。那么我们就可以定义不同的Interceptor来处理。这些拦截器之间没有任何关联,只关注自己处理过程。现在回头再去看看一些OKhttp的源码解读应该就很容易理解其精髓了。

  1. 检测升级,出现弹窗1
  2. 弹窗1关闭后,出现引导Window
  3. 引导图关闭后,出现选择语言弹窗。

如果需要是一步一步加上来的。比如第一个版本,我们只有检测升级。代码一定很直接,onCreate()方法里showUpgradeDialog()。当来了第二个需求,代码直接写就会变成这样。在升级弹窗的Dialog onDismissListenershowGuideWindow(),如果第一步未检测到升级,还需要判断if(!needUpgrade)直接showGuideWindow()。代码写到这里,这两个步骤已经耦合在一起了。引导window是否出现,跟检测弹窗几乎绑定在一起。如果有一天产品脑洞大开,我们先出引导再升级吧!想想你的代码会怎么改。
window的 onDismissListener 里showUpgradeDialog()?如果使用责任链模式呢?在控制层换一下顺序就好了。

总结

设计模式在开发实践中,对代码的可读,可扩展,可维护性起到重要作用。在面试中,经常会和面试者聊聊设计模式,大部分面试者都停留在设计概念本身,如果能阐述更多对设计模式的理解与在实践中的运用,相信会对你的面试有帮助。本文也给大家举一个设计模式学习的例子,从模式学习,思想理解到实践运用。回头再读一下本文重点:

如果还没有新的理解,留言给我。

上一篇下一篇

猜你喜欢

热点阅读