Retrofit源码赏析八 —— 设计模式

1,424 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

构建者模式

使用多个简单的对象一步步构建成一个复杂的对象,将构建复杂对象的过程和它的部件解耦,使构建过程和部件的表示隔离。

Retrofit中有许多配置,如果采用构造方法初始化则非常繁琐,这里采用构建者模式降低了使用的复杂度。

public static final class Builder {
    private final Platform platform;
    private @Nullable
    okhttp3.Call.Factory callFactory;
    private @Nullable
    HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable
    Executor callbackExecutor;
    private boolean validateEagerly;
}

使用的时候按需配置即可。

Retrofit.Builder().build()

单例模式

为一个类提供了访问其唯一对象的方法,确保内存中只会存在该类的唯一对象。 单例模式的实现方式比较多,一般采用的是DCL懒汉模式,减少不必要的同步开销。

static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
    static final RequestBodyConverter INSTANCE = new RequestBodyConverter();
}

Retrofit中直接采用了饿汉式的单例模式,实现比较简单。

外观模式

外观模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。

Retrofit中有许多配置项,比如Converter,CallAdapter,callFactory等参数,它采用外观模式提供了一个统一的入口Retrofit类,用来访问子系统中的一群接口,隐藏了系统的复杂性,使用起来更加灵活,我们只管配置Retrofit,至于内部各个子系统的联系无需关系。

Retrofit retrofit =
	new Retrofit.Builder()
	    .baseUrl(server.url("/"))
	    .addConverterFactory(new ToStringConverterFactory())
	    .build();

动态代理模式

给某一个对象提供一个代理,并由代理对象控制对原对象的引用并且代理类是在程序运行时创建的。

public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[]{service},new InvocationHandler() {
            public @Nullable  Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
               return ServiceMethod#invoke();
            }
        });
}

这里采用了JDK动态代理,所以先要验证是不是一个接口。在InvocationHandler.invoke()中拦截接口方法统一交给ServiceMethod及其子类处理。然后返回接口方法对应的类型。

静态代理模式

与动态代理模式不同的是,静态代理模式的代理类在编码阶段就确定了。

static final class ExecutorCallbackCall<T> implements Call<T> {
	ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
	public void enqueue(final Callback<T> callback) {
		//调用委托类 回调切换到主线程
	}
}

ExecutorCallbackCall类作为代理类控制了委托类OkHttpCall的执行,目的是将请求回调切换到主线程。

工厂模式

在创建对象时不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。

简单工厂模式

简单工厂模式又称为静态工厂方法模型,它可以根据参数的不同返回不同类的实例。简单工厂专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

class Platform {
	private static Platform findPlatform() {
	return "Dalvik".equals(System.getProperty("java.vm.name"))
	    ? new Android() 
	    : new Platform(true);
	}
}

findPlatform()就是一个静态工厂方法,它会创建出不同的Platform子类对象。

工厂方法模式

对于上面Platform来说,如果我们需要再增加一个平台比如IOS,则需要修改findPlatform()方法,但是这不符合开闭原则。

这时候需要将工厂类Factory的公共部分抽象出来,让每种产品都有自己的Factory子类,需要不同产品就调用不同的工厂类生产,这就是工厂模式。如果添加新产品的支持,只需要创建一个子Factory。

public interface CallAdapter<R, T> {
  abstract class Factory {
    public abstract  CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);
  }
}

Retrofit提供了CallAdapter对Call进行适配,让它支持不同的接口类型,需要什么样的CallAdapter,只需要用它的CallAdapter.Factory生产即可。

抽象工厂模式

在工厂方法模式中,一个产品对应一个工厂,一个工厂只能生产一个类型的产品,但是有的时候需要一个工厂生产多个产品,这时候就需要用到抽象工厂模式。

public interface Converter<F, T> {
	abstract class Factory {
		//请求结果转换器
	    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
	      return null;
	    }
		//请求参数转换器
	    public Converter<?, RequestBody> requestBodyConverter(Type type,Annotation[] pa,Annotation[] ma,Retrofit retrofit) {
	      return null;
	    }
		//字符串转换器
	    public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
	      return null;
	    }
	}
}

Converter.Factory能同时生产3个不同类型的转换器,如果使用工厂方法模式虽然也可以实现,但是这样就需要3个不同的工厂,在创建Retrofit的时候配置就很臃肿。

new Retrofit.Builder()
	.addRequestConverterFactory(new RequestConverterFactory())
	.addResponseConverterFactory(new ResponseConverterFactory())
	.addStringConverterFactory(new StringConverterFactory())
	.build()

适配器模式

将一个类的接口转换成希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

Retrofit最后的请求由OkHttp完成,它的响应结果和Retrofit需要的是不兼容的,而Converter就充当了Retrofit和OkHttp之间数据的桥梁,完成数据的转换。

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
        T body = responseConverter.convert(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        catchingBody.throwIfCaught();
        throw e;
    }
}

另外,OkHttp返回的是Call对象,而往往我们的是其他类型,比如配合RxJava时需要的是Observable对象,这时候就用到了CallAdapter。

public interface CallAdapter<R, T> {
	T adapt(Call<R> call);
}

享元模式

享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null)
        return result;
    synchronized (serviceMethodCache) {
        result = serviceMethodCache.get(method);
        if (result == null) {
            result = ServiceMethod.parseAnnotations(this, method);
            serviceMethodCache.put(method, result);
        }
    }
    return result;
}

serviceMethodCache是ConcurrentHashMap<Method, ServiceMethod<?>>类型,加载Method的时候,先从Map中查找,找到了就直接返回,如果没有找到,就创建一个新的,然后放入缓存池并返回该对象。