[关闭]
@Awille 2019-01-19T11:47:09.000000Z 字数 12548 阅读 137

设计模式与retrofit

Android 网络请求 Retrofit 设计模式


1、适配器模式

1.1 原理

适配器模式用于连接不匹配的二者之间,将不匹配变得匹配,类似于我们生活中使用的转换器、转接头。

1.2 适配器可以划分为三种模式:

1.2.1 类适配器模式

这种适配器模式是通过继承来实现的。
假设你目前在做一个项目,其中你设计了一个接口:

  1. public interface Demo {
  2. int methodA();
  3. }

这个接口的方法会返回你想要的数值。我们随便写一个实现

  1. public class DemoImpl implements Demo {
  2. @Override
  3. public int myMethod() {
  4. return 5;
  5. }
  6. }

现在来了一个普通的客户,它想调用你这个接口的方法得到那个值

  1. public class NormalClient {
  2. public int satisfyMyNormalRequest(Demo demo) {
  3. return demo.myMethod();
  4. }
  5. }

十分简单,在他的方法当中传入我们的接口实例就可以了。
但是客户越来越多,出现了不正常的用户,这个用户说,他需要我们提供那个值,但是他有自己的规范的,比如他的方法是这样的

  1. public class AbNormalClient {
  2. public int satisfyMyAbNormalRequest(MyDemo demo) {
  3. return demo.methodForMySelf();
  4. }
  5. }

这是他的接口规范:

  1. public interface MyDemo {
  2. int methodForMySelf();
  3. }

现在要满足他的需求已经不能传入我们自己的接口实例了,咋办呢?此时类适配器模式就派上用场了。
我们专门为这个客户写一个适配器,该适配器继承我们的接口实现类,并且实现客户的接口规范

  1. public class AdapterForClient extends DemoImpl implements MyDemo{
  2. @Override
  3. public int methodForMySelf() {
  4. return myMethod();
  5. }
  6. }

这个适配器继承了我们自己的接口实现类,那么这个适配器就可以调用我们实现类中的方法了,而在实现客户自己的接口时,我们就可以调用自己的实现类中的方法返回相应的值。
这个适配器也是MyDemo接口的一个实例,客户可以直接在他们的方法当中建一个改适配器的实例就可以了。

1.2.2 对象适配器模式

对象适配器模式跟类适配器模式是同一个道理,只是我们不通过继承来实现

  1. public class Adapter2 implements MyDemo {
  2. Demo demo;
  3. public Adapter2(Demo demo) {
  4. this.demo = demo;
  5. }
  6. @Override
  7. public int methodForMySelf() {
  8. return demo.myMethod();
  9. }
  10. }

看到这个实现类就应该知道是怎么回事,对象适配器模式呢,就是把我们自己的接口实现类的实例作为适配器的成员变量,这样适配器中直接可以通过该实例调用相应的方法

1.2.3 接口适配器模式

接口适配器特别简单,假设目前有一个接口

  1. public interface Test {
  2. void test1();
  3. void test2();
  4. void test3();
  5. void test4();
  6. void test5();
  7. }

这个接口当中有很多的方法,当我想创建一个接口实例的时候,我必须得有个实现类实现所有的方法,但有的时候我并不想实现所有的方法,那么此时,我可以先写个接口适配器

  1. public class InterfaceAdapter implements Test {
  2. @Override
  3. public void test1() {
  4. }
  5. @Override
  6. public void test2() {
  7. }
  8. @Override
  9. public void test3() {
  10. }
  11. @Override
  12. public void test4() {
  13. }
  14. @Override
  15. public void test5() {
  16. }
  17. }

这个类啥也不做,调用的时候:

  1. public class Client {
  2. public void satisfyRequest(Test test) {
  3. test.test1();
  4. }
  5. public static void main(String[] args) {
  6. Client client = new Client();
  7. client.satisfyRequest(new InterfaceAdapter(){
  8. @Override
  9. public void test1() {
  10. System.out.println("HELLO WORLD");
  11. }
  12. });
  13. }
  14. }

2、动态代理

在静态代理当中,代理类是在程序运行前就已经确定下来并且编译完成的,而动态代理是程序运行时根据我们的指示动态生成的。在代理模式当中,代理类与委托类有同样的接口,代理类一般是用来将消息传递给委托类之前做一些消息预处理,以及在委托类处理完消息后,代理类做一些事后的处理。动态代理的实现原理是反射。
java的动态代理实现,需要注意一个接口InvocationHandler 跟 一个类 Proxy。

每一个动态代理类都要实现InvocationHandler接口,当动态代理类调用委托类的方法时,都会通过这个接口的invoke方法来实现。

  1. public interface InvocationHandler {
  2. // proxy: 我们代理的真实对象,即我们的委托类
  3. // method: 委托类中的某一个方法
  4. // args: 委托类中对应方法需要传入的参数
  5. public Object invoke(Object proxy, Method method, Object[] args)
  6. throws Throwable;
  7. }

Proxy类当中有一个newProxyInstance用于创建我们的动态代理对象:

  1. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  2. //loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  3. //interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  4. //h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

2.1 demo

定义一个接口,该接口可能会有多种实现:

  1. public interface Subject {
  2. public void rent();
  3. public void hello(String str);
  4. }

该接口的实现,同时也是我们实现的代理类之一:

  1. public class RealSubject implements Subject {
  2. @Override
  3. public void rent() {
  4. System.out.println("I want to rent my house");
  5. }
  6. @Override
  7. public void hello(String str) {
  8. System.out.println("hello " + str);
  9. }
  10. }

动态代理类的创建:

  1. public class DynamicProxy implements InvocationHandler {
  2. //我们代理的真实对象,即我们的委托类,
  3. // 我们暂时不确定他是什么类对象,所暂时用Object来声明
  4. private Object subject;
  5. public DynamicProxy(Object subject) {
  6. this.subject = subject;
  7. }
  8. @Override
  9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10. System.out.println("消息预处理");
  11. method.invoke(subject, args);
  12. System.out.println("事后消息处理");
  13. return null;
  14. }
  15. }

测试:

  1. public class ClientTest {
  2. public static void main(String[] args) {
  3. //代理的真实对象
  4. Subject realSubject = new RealSubject();
  5. InvocationHandler handler = new DynamicProxy(realSubject);
  6. Subject myProxy = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
  7. realSubject.getClass().getInterfaces(),
  8. handler);
  9. myProxy.rent();
  10. myProxy.hello("world");
  11. }
  12. }

输出:

消息预处理
I want to rent my house
事后消息处理
消息预处理
hello world
事后消息处理

这个动态代理达到效果,可以考虑那么一种情景,这上面例子中的subject接口当中,存在很多的方法。我们想要计算每个方法调用消耗的时间,如果采用一般的方法,首先持有接口对象,然后通过该接口对象调用相关的方法,那么有n个方法,我们就要写n次计算时间的逻辑。而动态代理当中,所有的委托类的方法调用都是通过委托类中的InvocationHandler的invoke方法实现的,那么我们只需在invoke方法中把计算时间的逻辑填充上去,就能完成对所有方法的时间计算了。

3、策略模式

看了下策略模式,感觉跟依赖注入谜之相似,这里就不写了

4、Retrofit中的设计模式

4.1 建造者模式

我们平时创建retrofit对象就是通过retrofit的builder对象来创建的。

  1. Retrofit retrofit = new Retrofit.Builder()
  2. .baseUrl("https://api.github.com/")
  3. .addConverterFactory(GsonConverterFactory.create())
  4. .build();

4.2 retrofit中的动态代理模式

当创建我们的API实例的时候,我们使用了这行代码:

  1. GithubService service = retrofit.create(GithubService.class);

查看其实现方式:
看到这里是使用了动态代理的

  1. public <T> T create(final Class<T> service) {
  2. Utils.validateServiceInterface(service);
  3. if (validateEagerly) {
  4. eagerlyValidateMethods(service);
  5. }
  6. return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
  7. new InvocationHandler() {
  8. //先暂时省略该接口的实现
  9. });
  10. }

看到在Proxy.newProxyInstance传入的三个对象:
classloader: 传入我们传入的API接口的classloader对象
interfaces: 传入我们API中定义的接口
InvocationHandler: 该接口的实现类

我们看下invocationHandler的实现:

  1. new InvocationHandler() {
  2. private final Platform platform = Platform.get();
  3. @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
  4. throws Throwable {
  5. // 如果调用的方法是Object中的方法,如equals, toString等直接用平常我们使用的返回
  6. if (method.getDeclaringClass() == Object.class) {
  7. return method.invoke(this, args);
  8. }
  9. //如果是 default 方法(Java 8 引入),就调用 default 方法
  10. if (platform.isDefaultMethod(method)) {
  11. return platform.invokeDefaultMethod(method, service, proxy, args);
  12. }
  13. //我们实际上的API接口定义的方法都是通过下面的三行代码实现的
  14. ServiceMethod<Object, Object> serviceMethod =
  15. (ServiceMethod<Object, Object>) loadServiceMethod(method);
  16. OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
  17. return serviceMethod.adapt(okHttpCall);
  18. }
  19. }

通过以上分析,我们知道了我们如果调用API实例中的方法(一般是返回某种call对象),最终都会通过这三行代码实现:

  1. ServiceMethod<Object, Object> serviceMethod =
  2. (ServiceMethod<Object, Object>) loadServiceMethod(method);
  3. OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
  4. return serviceMethod.adapt(okHttpCall);

首先创建了一个 ServiceMethod对象,ServiceMethod的作用是这样描述的:
Adapts an invocation of an interface method into an HTTP call.
很清晰,作用就是把我们API接口方法的调用转化成一个HTTP call对象

  1. ServiceMethod#loadServiceMethod
  2. ServiceMethod<?, ?> loadServiceMethod(Method method) {
  3. //缓存逻辑,之前获取过的会被缓存下来,下次调用相同的方法会返回缓存中记录的ServiceMethod对象
  4. ServiceMethod<?, ?> result = serviceMethodCache.get(method);
  5. if (result != null) return result;
  6. //锁 缓存在同一时间只允许被一个线程访问
  7. synchronized (serviceMethodCache) {
  8. result = serviceMethodCache.get(method);
  9. if (result == null) {
  10. //创建servicemethod对象
  11. result = new ServiceMethod.Builder<>(this, method).build();
  12. //缓存
  13. serviceMethodCache.put(method, result);
  14. }
  15. }
  16. return result;
  17. }

ServiceMethod的构造方法:

  1. ServiceMethod(Builder<R, T> builder) {
  2. //负责创建 HTTP 请求
  3. this.callFactory = builder.retrofit.callFactory();
  4. //把retrofit2.Call<T> 对象转换成为T;
  5. //整个流程当中,首先会发送Http请求,Http请求返回后,
  6. //将数据对象转换成T类型实例,通过Converter<R, T>实现
  7. this.callAdapter = builder.callAdapter;
  8. this.baseUrl = builder.retrofit.baseUrl();
  9. //上面提到的将服务端返回数据转换成T对象的转换器,
  10. // 石基类型是Converter<Response, T>
  11. this.responseConverter = builder.responseConverter;
  12. this.httpMethod = builder.httpMethod;
  13. this.relativeUrl = builder.relativeUrl;
  14. this.headers = builder.headers;
  15. this.contentType = builder.contentType;
  16. this.hasBody = builder.hasBody;
  17. this.isFormEncoded = builder.isFormEncoded;
  18. this.isMultipart = builder.isMultipart;
  19. //负责将API调用时各种请求参数转换成Http request中传入的参数
  20. this.parameterHandlers = builder.parameterHandlers;
  21. }

ServiceMethod中有一个parameterHandlers对象,这里就涉及到为什么retrofit要使用动态代理,parameterHandlers是负责网络调用的request创建时各种参数传入的。我们知道,在retrofit当中,参数的标注很多是通过注解来标注的,动态代理的使用对retrofit最大的作用就是,由于所有的方法调用都会转移到到InvocationHandler的invoke中来调用,那么这里就可以统一的来做对注解的处理了。以我目前的理解来看,动态代理模式的使用好处对retrofit来说就是方便统一对注解进行处理。

4.3 工厂模式:

我们主要关注以下两个:
callFactory: 负责创建Http请求
callAdapter: 负责发送Http请求,并将从服务端返回的数据通过responseConverter转化成为我们定义的T类型实例对象

刚刚说到的三行代码当中,构建完ServiceMethod对象以后:

  1. OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

okhttpcall对象实现了call接口:

  1. final class OkHttpCall<T> implements Call<T>

call接口的方法跟okhttp一样的

  1. //同步
  2. Response<T> execute() throws IOException;
  3. //异步
  4. void enqueue(Callback<T> callback);
  5. boolean isExecuted();
  6. void cancel();
  7. //返回request对象
  8. Request request();

我们先只关注execute方法的实现,execute跟enqueue方法的实现主要原理上应该跟okhttp相同,而且,enqueue方法实质上也就是新开了线程执行execute方法,在执行结束后通过回调返回相应的结果。毕竟retrofit只是在okhttp上做了一层封装,我们直接看一个简单的实现,看看retrofit的差异在哪里就可以了。

  1. @Override public Response<T> execute() throws IOException {
  2. okhttp3.Call call;
  3. call = rawCall;
  4. if (call == null) {
  5. try {
  6. call = rawCall = createRawCall();
  7. } catch (IOException | RuntimeException | Error e) {
  8. throwIfFatal(e); // Do not assign a fatal error to creationFailure.
  9. creationFailure = e;
  10. throw e;
  11. }
  12. }
  13. }
  14. return parseResponse(call.execute());
  15. }

createRawCall方法:

  1. private okhttp3.Call createRawCall() throws IOException {
  2. okhttp3.Call call = serviceMethod.toCall(args);
  3. if (call == null) {
  4. throw new NullPointerException("Call.Factory returned null.");
  5. }
  6. return call;
  7. }

可以看到这里的call对象其实调用的就是okhttp的call方法。
整个execute方法差异主要就是在返回的那部分:
parseResponse(call.execute()):
在okhttp中call.execute()返回的是服务端返回的reponse对象,这里函数应该是将该reponse对象通过我们上面提到的servicemethod的converter来实现转换:

  1. Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  2. ResponseBody rawBody = rawResponse.body();
  3. //省略状态码异常的处理,只关注状态码为200的返回
  4. ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  5. try {
  6. //这里是做服务端数据到我们定义的返回类型的转换的:
  7. T body = serviceMethod.toResponse(catchingBody);
  8. return Response.success(body, rawResponse);
  9. } catch (RuntimeException e) {
  10. // If the underlying source threw an exception, propagate that rather than indicating it was
  11. // a runtime exception.
  12. catchingBody.throwIfCaught();
  13. throw e;
  14. }
  15. }

做数据转换的时候用到了servicemethod的toResponse方法:

  1. R toResponse(ResponseBody body) throws IOException {
  2. return responseConverter.convert(body);
  3. }

这里果然用到的就是我们之前在servicemethod方法当中提到的converter。

4.4 适配器模式

在我们一直提到的那三行代码当中,最后一行是:

  1. return serviceMethod.adapt(okHttpCall);

adapt方法的实现为:

  1. T adapt(Call<R> call) {
  2. return callAdapter.adapt(call);
  3. }

retrofit是支持返回rxjava用到的Obeservable对象的。这里转换用到的就是我们之前早serviceMethod中的callAdapter来实现的,callAdapter专门用来做不同call对象的转换。

在使用之前,我们创建retrofit的时候,要把rxjava的RxJavaCallAdapterFactory添加到retrofit的CallAdapterFactory中,这个添加会被加到retrofit的CallAdapaterfactory列表中:

  1. private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();

最终这个列表中的callAdapterFactory会根据我们在API实例中定义的返回类型,决定在创建serviceMethod会用哪个具体的CallAdapter

  1. private CallAdapter<T, R> createCallAdapter() {
  2. Type returnType = method.getGenericReturnType();
  3. //省略部分代码
  4. Annotation[] annotations = method.getAnnotations();
  5. try {
  6. //这里根据类型来决定使用哪个adapter
  7. return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
  8. } catch (RuntimeException e) { // Wide exception range because factories are user code.
  9. throw methodError(e, "Unable to create call adapter for %s", returnType);
  10. }
  11. }

假如我们没有传入CallAdapterFactory,那么使用的是DefaultCallAdapterFactory,该adapter直接返回传入的参数啥也不做。ExecutorCallAdapterFactory也是有一个内置的adapter,生产的 adapter 会在异步调用时在指定的 Executor 上执行回调

我们关注下rxjava的adapter,只关注其对Observable对象返回的支持。
以下方式是RxjavaCallAdapter的内部类SimpleAdapter的实现:

  1. @Override public <R> Observable<R> adapt(Call<R> call) {
  2. Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
  3. .lift(OperatorMapResponseToBodyOrError.<R>instance());
  4. if (scheduler != null) {
  5. return observable.subscribeOn(scheduler);
  6. }
  7. return observable;
  8. }

发现往后深入需要对Rxjava的源码也具体的了解才行,所以不继续看实现流程了。探讨一下这里适配器模式的实现。

适配器模式有三种,类适配器模式、对象适配模式、接口适配器模式。粗略的看,这里应该用的不是接口适配器模式,毕竟接口适配器模式不是用于这种场景的。
首先在调用的时候:

  1. T adapt(Call<R> call) {
  2. return callAdapter.adapt(call);
  3. }

这里的callAdapter的定义是一个接口:

  1. private final CallAdapter<R, T> callAdapter;

该接口的定义为:

  1. //R原始类型,T适配后的类型
  2. public interface CallAdapter<R, T> {
  3. //泛型包裹的真实对象类型,比如Call<A>则就是A的Type对象
  4. Type responseType();
  5. //返回一个T类型来代理Call,
  6. //这里返回类型是根据我们的接口定义的返回来决定的,可以使okhttpcall,可以是observable
  7. T adapt(Call<R> call);
  8. abstract class Factory {
  9. //根据接口返回特定的CallAdapter
  10. public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
  11. Retrofit retrofit);
  12. protected static Type getParameterUpperBound(int index, ParameterizedType type) {
  13. return Utils.getParameterUpperBound(index, type);
  14. }
  15. protected static Class<?> getRawType(Type type) {
  16. return Utils.getRawType(type);
  17. }
  18. }
  19. }

其内部有个Factory接口,其中的方法决定使用哪个CallAdapter。
CallAdapter的adapt方法决定返回的call对象

假如你想返回一个自定义类型的Call对象,叫做MyCall:

  1. public class MyCallAdapter <T> implements CallAdapter<T, HttpCall<T>> {
  2. private final Type responseType;
  3. private final Executor callbackExecutor;
  4. HttpCallAdapter(Type responseType, Executor callbackExecutor) {
  5. this.responseType = responseType;
  6. this.callbackExecutor = callbackExecutor;
  7. }
  8. @Override
  9. public Type responseType() {
  10. return responseType;
  11. }
  12. @Override
  13. public MyCall<T> adapt(Call<T> call) {
  14. return new MyCall<>(call, callbackExecutor);
  15. }
  16. }

在retrofit中接口中方法定义时,每一个返回对象的都要有一个adapter与之相对应,比如okhhtpcall对象对应defaultCallAdapter, observable对应rxjavaadapterFactory中的simpleAdapter。

小结:
到上面为止,好像都跟类适配模式或者对象适配器模式不对应。感觉这种根据接口方法定义的返回类型选择不同的callAdapter,然后调用它们的adapt得到返回对象,这更像是策略模式。

我想这里的适配器模式,真正的应用应该是在:
(下面说的okhttpcall对象就是retrofit.call对象)
retrofit适配是从okhttpcall对象到我们自定义或者其他call对象的转换。要从okhttpcall对象转换为我们自定义的call对象,那么首先适配器类要持有okhttpcall对象,然后实现我们自定义call对象的接口,在实现自定义call对象接口中使用持有的okhttpcall实例对象进行相关的操作。

Tips:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注