优雅地烘焙 Retrofit

前言

将构造 Retrofit 时所需要的材料隔离开来,利用依赖倒置这个原则,优雅地烘烤出美味的 Retrofit 实例。

类图

来自《Head First 设计模式》抽象工厂一节中披萨店卖披萨的启发,我们也将 Retrofit 看做一样商品,来设计它。

RetrofitWrapper

我们需要的产品,内部包含了一个 Retrofit 的引用。我们将这个类抽象,构造 Retrofit 的原料来自另一个抽象类:RetrofitWrapperFactory,代码如下:

/**
 * 产品类,包装了一个 Retrofit
 */
public abstract class RetrofitWrapper {

  protected Retrofit mRetrofit;

  public RetrofitWrapper(RetrofitWrapperFactory retrofitWrapperFactory) {

    mRetrofit = new Retrofit.Builder().baseUrl(retrofitWrapperFactory.provideBaseUrl())
        .client(retrofitWrapperFactory.createOkHttpClient()) // 提供 OkHttp 实例
        .addConverterFactory(retrofitWrapperFactory.createGsonConverterFactory()) // 提供 GsonConvertFactory 实例
        .addCallAdapterFactory(retrofitWrapperFactory.createRxJavaCallAdapter()) // 提供RxJavaCallAdapter 的实例
        .build();
  }

  public Retrofit getRetrofit() {

    return mRetrofit;
  }
}

RetrofitWrapperFactory

/**
 * RetrofitWrapper 抽象工厂,提供 RetrofitWrapper 需要的原料
 */
public interface RetrofitWrapperFactory {

  String provideBaseUrl();

  OkHttpClient createOkHttpClient();

  Converter.Factory createGsonConverterFactory();

  CallAdapter.Factory createRxJavaCallAdapter();
}

RetrofitWrapperStore

兜售 RetrofitWrapper 的商店,这里商店类还继承了一个 RetrofitApiProvider 接口,用来对外暴露创建 Retrofit Api Service 的接口。

/**
 * RetrofitWrapper 商店类
 */
public abstract class RetrofitWrapperStore implements RetrofitApiProvider {

  protected abstract RetrofitWrapper createRetrofitWrapper();
}
public interface RetrofitApiProvider {

  <T> T provideApi(Class<T> service);
}

举栗时间

具体产品类

public class PeroRetrofitWrapper extends RetrofitWrapper {

  public PeroRetrofitWrapper(RetrofitWrapperFactory retrofitWrapperFactory) {
    super(retrofitWrapperFactory);
  }
}

具体工厂类

public class PeroRetrofitWrapperFactory implements RetrofitWrapperFactory {

  private static final int DEFAULT_CONNECT_TIME_OUT = 10; // 默认超时时间

  private static final String TEST_SERVER_URL = "http://192.168.199.182:3000";

  @Override public OkHttpClient createOkHttpClient() {

    return new OkHttpClient.Builder().connectTimeout(DEFAULT_CONNECT_TIME_OUT, TimeUnit.SECONDS)
        .addInterceptor(provideHttpLoggingInterceptor())
        .addNetworkInterceptor(provideTokenInterceptor())
        .build();
  }

  private Interceptor provideHttpLoggingInterceptor() {

    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    return httpLoggingInterceptor;
  }

  private Interceptor provideTokenInterceptor() {

    return new TokenInterceptor();
  }

  @Override public CallAdapter.Factory createRxJavaCallAdapter() {

    return RxJavaCallAdapterFactory.create();
  }

  @Override public Converter.Factory createGsonConverterFactory() {

    Gson gson =
        new GsonBuilder().registerTypeAdapterFactory(provideParamsAdapterFactory()).create();

    return GsonConverterFactory.create(gson);
  }

  private TypeAdapterFactory provideParamsAdapterFactory() {

    return new ParamsAdapterFactory();
  }

  @Override public String provideBaseUrl() {

    return TEST_SERVER_URL;
  }
}

具体商店类

public final class PeroRetrofitWrapperStore extends RetrofitWrapperStore {

  private RetrofitWrapper mRetrofitWrapper;

  public PeroRetrofitWrapperStore() {

    mRetrofitWrapper = createRetrofitWrapper();
  }

  private static class SingletonHolder {

    private static final PeroRetrofitWrapperStore sInstance = new PeroRetrofitWrapperStore();
  }

  public static PeroRetrofitWrapperStore getInstance() {

    return SingletonHolder.sInstance;
  }

  @Override protected RetrofitWrapper createRetrofitWrapper() {

    return new PeroRetrofitWrapper(new PeroRetrofitWrapperFactory());
  }

  @Override public <T> T provideApi(Class<T> service) {

    return mRetrofitWrapper.getRetrofit().create(service);
  }
}

如何调用?

在实际项目中,PeroRetrofitWrapperPeroRetrofitWrapperFactory 可以作为 PeroRetrofitWrapperStore 的静态内部类,从而屏蔽外部可见性。毕竟,现实生活中,我们买披萨也是去商店购买,而不会跑到原料工厂自己动手造披萨对吧2333。

// 获取手机验证码
private void fetchPinCode(final String phoneNum) {

  PeroRetrofitWrapperStore.getInstance()
      .provideApi(PeroApi.UserApi.class)
      .fetchSmsCode(new Params.Builder().put("phoneNumber", phoneNum).create())
      // 省略...

思考&改进

示例代码里已经将 RetrofitWrapper 单例化了,而创建 Service Api,其实还是在 new object

@Override public <T> T provideApi(Class<T> service) {

    return mRetrofitWrapper.getRetrofit().create(service);
  }

如果有需要,也可以将 Api Service 进行单例化。

总结

new 关键字的地方我们就应该谨慎,是否耦合得太厉害,是否可以用工厂模式来解耦。当然,因为我的项目里暂时还没有 Dagger2,所以自己动手,实现解耦,如果有 Dagger2 的话,就不用这么麻烦了233。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏工科狗和生物喵

【计算机本科补全计划】CCF计算机职业资格认证 201709-01/02详解

正文之前 貌似我找到的那个题目网站更新了一波最新的题目。不过201709 只有1、2 题,所以先做了吧(其实是我自己对能不能做出第三题 持怀疑态度!)宝宝心里苦...

32260
来自专栏写代码的海盗

乐呵乐呵得了 golang入坑系列

开场就有料,今天返回去看了看以前的文章,轻松指数有点下降趋势。一琢磨,这不是我的风格呀。一反思,合着是这段时间,脑子里杂七杂八的杂事有点多,事情一多,就忘了快乐...

35950
来自专栏HansBug's Lab

3407: [Usaco2009 Oct]Bessie's Weight Problem 贝茜的体重问题

3407: [Usaco2009 Oct]Bessie's Weight Problem 贝茜的体重问题 Time Limit: 3 Sec  Memory ...

29680
来自专栏大数据学习笔记

Flink学习笔记:1、Flink快速入门

官方文档:https://ci.apache.org/projects/flink/flink-docs-release-1.3/quickstart/setu...

1.3K100
来自专栏码农阿宇

NopCommerce开源项目中很基础但是很实用的C# Helper方法

43430
来自专栏大数据挖掘DT机器学习

使用R语言构造投资组合

原作者: 邓一硕 来自: 格物堂 构造投资组合是金融投资分析中历久弥新的问题。多年以来,学界、业界提出诸多对投资组合进行优化的方法。比如,最经典的基于收益率均...

59960
来自专栏上善若水

015android初级篇之传感器的简单使用

要监控传感器的原始数据,你需要实现 SensorEventListener 接口的 onAccuracyChanged() 和onSensorChanged()...

15350
来自专栏小樱的经验随笔

BZOJ 1061: [Noi2008]志愿者招募【单纯形裸题】

1061: [Noi2008]志愿者招募 Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 4813  Solv...

36450
来自专栏菩提树下的杨过

Silverlight:双向绑定综合应用-多集合的依赖绑定

这是上一篇“Silverlight:双向绑定综合应用-自动更新集合汇总字段”的续篇。需求场景如下: 一个公司,有N个员工,逢年过节时要搞一些抽奖活动,最终要公告...

24760
来自专栏码农阿宇

NopCommerce开源项目中很基础但是很实用的C# Helper方法

刚过了个五一,在杭州到处看房子,不知道杭州最近怎么了,杭州买房的人这么多,房价涨得太厉害,这几年翻倍翻倍地涨,刚过G20,又要亚运会,让我这样的刚需用户买不起,...

429110

扫码关注云+社区

领取腾讯云代金券