Android 网络框架--Retrofit2 架构分析

2017-09-21  本文已影响146人  嘎啦果安卓兽

面向接口编程

面向接口编程,模块化编程的必备技能,其乃实现解耦,增强扩展性的重要手段。

面向接口编程具体指的是什么呢?

首先说一下什么是面向对象编程,大家都知道,面向对象编程是相对于面向过程编程来说的,基本上,具有对象概念的程序设计都可以称为面向对象编程。而面向接口编程仅仅是面向对象编程的一种模块化实现形式,其从组件的角度来进行代码设计,将抽象与实现分离。

接口泛指实体把自己提供给外界的一种抽象物,利用由内部操作分离出的外部沟通方法,使得实体能被修改内部而不影响外界其他实体与自己交互的方式。面向接口编程中的“接口”,在Java语言中,不仅仅是“interface”关键字这么简单,interface、abstract class以及普通的class都能成为所谓的接口。

interface约定的是务必实现的方法,强调的是规则的制定。abstract class则是在抽象的同时允许提供一些默认的行为,以达到代码复用的效果。一个实现类(相对于抽象而言)可以实现多个interface,而只能继承一个abstract class。

面向接口编程能够带来什么呢?

面向接口编程可以降低代码间的耦合,增强代码的扩展性,而正是这种特性,使得多人同时开发变成了可能。其实大部分设计模式就是面向接口编程很好的例子。

面向接口编程如何实现呢?

进行面向接口编程实操时,我们一般注意三点:先定义接口(接口或者抽象类),再定义实现类;在函数间传入传出的是接口而不是实现类;一方只认识另一方的接口,想进入另一方,就去调用另一方所披上的接口外套。

从下图中我们可以看到Retrofit用到了哪些接口类:


Retrofit接口类.png

设计模式

我们从Retrofit的调用流程来分析用到的设计模式

创建Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

建造者模式--摘自《Java与模式》
建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。
在很多情况下,建造者模式实际上是将一个对象的性质建造过程外部化到独立的建造者对象中,并通过一个导演者角色对这些外部化的性质赋值过程进行协调。

以下情况使用建造者模式

创建网络请求接口的实例

GitHub github = retrofit.create(GitHub.class);

门面模式--摘自《Java与模式》
门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行,门面提供一个高层次的接口,使得子系统更易于使用
门面模式的门面类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与门面对象打交道,而不需要与子系统内部的很多对象打交道

以下情况使用门面模式

创建网络请求接口中方法的Call实例

代码块一
Call<List<Contributor>> call = github.contributors("square", "retrofit");
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
    throws Throwable {
  // If the method is a method from Object then defer to normal invocation.
  if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
  }
  if (platform.isDefaultMethod(method)) {
    return platform.invokeDefaultMethod(method, service, proxy, args);
  }
  ServiceMethod<Object, Object> serviceMethod =
      (ServiceMethod<Object, Object>) loadServiceMethod(method);
  OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
  return serviceMethod.callAdapter.adapt(okHttpCall);
}

代理模式(本代码块为动态代理)--摘自《Java与模式》
代理主题并不改变主题的接口,因为模式的用意是不让客户端感觉到代理的存在
代理使用委派将客户端的调用委派给真实的主题对象,换言之,代理主题起到的是一个传递请求的作用
代理主题在传递请求之前和之后都可以执行特定的操作,而不是单纯传递请求

以下情况使用代理模式

代码块二
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
  int start = adapterFactories.indexOf(skipPast) + 1;
  for (int i = start, count = adapterFactories.size(); i < count; i++) {
    CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }

策略模式--摘自《Java与模式》
其是对算法的包装,是把使用算法的责任和算法本身分隔开,委派给不同的对象管理。
其通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。即,准备一组算法,并将每个算法封装到具有共同接口的独立的类中,使得它们可以互换。
其并不决定在何时使用何种算法,应当由客户端自己决定在什么情况下使用什么具体策略角色。

以下情况使用策略模式

代码块三
return new CallAdapter<Object, Call<?>>() {
  @Override public Type responseType() {
    return responseType;
  }

  @Override public Call<Object> adapt(Call<Object> call) {
    return new ExecutorCallbackCall<>(callbackExecutor, call);
  }
};

@Override public Object adapt(Call<R> call) {
  OnSubscribe<Response<R>> callFunc = isAsync
      ? new CallEnqueueOnSubscribe<>(call)
      : new CallExecuteOnSubscribe<>(call);

  OnSubscribe<?> func;
  if (isResult) {
    func = new ResultOnSubscribe<>(callFunc);
  } else if (isBody) {
    func = new BodyOnSubscribe<>(callFunc);
  } else {
    func = callFunc;
  }
  Observable<?> observable = Observable.create(func);

  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }

  if (isSingle) {
    return observable.toSingle();
  }
  if (isCompletable) {
    return observable.toCompletable();
  }
  return observable;
}

适配器模式--摘自《Java与模式》
把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

以下情况使用适配器模式

发送网络请求

static final class ExecutorCallbackCall<T> implements Call<T> {
  final Executor callbackExecutor;
  final Call<T> delegate; //注意,变量类型是父类的类型

  ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
  }

  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");
 
    delegate.enqueue(new Callback<T>() {
      @Override public void onResponse(Call<T> call, final Response<T> response) {
        callbackExecutor.execute(new Runnable() {
          @Override public void run() {
            if (delegate.isCanceled()) {
              // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
              callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
            } else {
              callback.onResponse(ExecutorCallbackCall.this, response);
            }
          }
        });
      }

      @Override public void onFailure(Call<T> call, final Throwable t) {
        callbackExecutor.execute(new Runnable() {
          @Override public void run() {
            callback.onFailure(ExecutorCallbackCall.this, t);
          }
        });
      }
    });
  }
}

装饰模式--摘自《Java与模式》
以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同
可以在不使用创造更多子类的情况下,将对象的功能加以扩展,是继承关系的一个替代方案
纯粹的装饰模式很难找到,装饰模式用意在不改变接口的前提下,增强被装饰类的性能,即要满足透明性。在增强性能的时候,往往需要建立新的公开方法。这就导致了大多数的装饰模式的实现都是“半透明”的。这意味着客户端不去声明抽象父类类型的变量,而是声明具体装饰类类型的变量,从而可以调用具体装饰类中才有的方法。

以下情况使用装饰模式

对于这一块代码,网络上也有人将其归类为代理模式,本兽下面说一下自己的观点。
装饰模式、适配器模式、代理模式都是包装(Wrapper)模式,首先说一下他们之间的区别。

本兽认为,这一块代码,将其归类为装饰模式或者代理模式,都是可以的。因为设计模式本就是属于概念性的,指导性的范畴,各个设计模式其实没有那么清晰的界限,没必要非要分个你清我楚,只要都满足基本的设计原则(S.O.L.I.D)即可。

但如果非要较真(程序员的特点),到底偏向于装饰模式还是偏向于代理模式,本兽认为其更偏向于装饰模式,进一步来讲,其更偏向于简化后的装饰模式。

装饰模式简易类图如下:


装饰模式简略类图.png

简化后的装饰模式简易类图如下:


简化后的装饰模式简易类图.png

代理模式简易类图如下:


代理模式简易类图.png

大家可以看到,简化后的装饰模式的简易类图和代理模式的简易类图,非常相像。相比较之后,不同点如下:

根据上述区别,再对应到代码块,我们就可以得出结论,其更偏向于简化后的装饰模式。

处理返回数据

NA

总结

Retrofit更像是一个组织者,他把几个框架高效的组合起来,在解耦的同时也满足了扩展性:其利用OkHTTP进行网络请求;与异步请求框架和类解析框架解耦,使得Retrofit可以适配多种框架,使用者可以轻松的选择或者自创适合自己项目的异步请求和解析的框架。

读者可以好好体会一下其是如何玩转各种设计模式,把面向接口编程发挥得淋漓尽致的。

Retrofit 2.0之后网络只支持OKHttp,对OKHttp强依赖。以后如果有新的网络框架出现,将无法使Retrofit。但是Retrofit提供的封装网络框架的思路依然值得借鉴。


上一篇 下一篇

猜你喜欢

热点阅读