细细品读 Retrofit 的设计之美 二

细细品读 Retrofit 的设计之美 二

作者 | JerryloveEmily

地址 | http://www.jianshu.com/p/dab7f5720aa5

声明 | 本文是 JerryloveEmily 原创,已获授权发布,未经原作者允许请勿转载



引言

在上一篇 细细品读 Retrofit 的设计之美一 后,我们了解了 Builder 构建者模式和(动态)代理模式在 Retrofit 中的做用,以及它们的使用套路。今天继续品读 Retrofit 框架值得我们好好思考的设计:抽象工厂模式


抽象工厂模式

在看 Retrofit 的抽象工厂模式的应用前,先来了解下,抽象工厂模式的套路,不扯虚的直接举一个实用的例子:


我们都知道作为 app 开发者,通常的 app 应用都会有用户系统,一个用户系统往往都包含了以下模块:1. 登录模块。 2. 注册模块。 3. 找回密码模块。 4. 用户个人信息模块。


这几个模块代表工厂需要生产的不同类型的产品,用户系统帐号,我们可能是app自身的帐号、密码、或者手机短信验证码的登录方式,也可能是第三方平台帐号登录:微信、QQ、新浪微博等等。对于不同的平台的用户帐号我们可以看做不同的品牌工厂,比如:app自身的用户帐号工厂、微信的用户帐号工厂、QQ的用户帐号工厂、新浪微博的用户帐号工厂。


这样来设计一个用户系统是不是更清晰点,而且不同的品牌的工厂便于替换,也就是替换登录的平台,不同的产品模块类的功能职责也变的比较单一符合设计模式的单一原则。


案例实现

  1. 首先抽象出各个产品接口出来,每种模块产品都有各自的功能


细细品读 Retrofit 的设计之美 二


细细品读 Retrofit 的设计之美 二


这些产品模块的接口规范功能抽象,对于 app 的用户系统来说基本够用了。当然上面的这些接口,也可以统一用一个接口文件来写,这些模块就作为子接口嵌套在里面,这是为了方便管理。

  1. 然后是工厂的抽象接口,用于生产不同品牌的不同产品


细细品读 Retrofit 的设计之美 二


主要就是获取不同模块的产品抽象接口对象,便于客户端使用工厂的模块对象的时候多态性。

  1. 实现不同登录方式的工厂和具体的用户系统模块
    因为用户系统大部分情况下都需要和 UI 交互,所以封装了一层基类把 Context 上下文统一起来,减少子类的不必要的重复。


细细品读 Retrofit 的设计之美 二


比如,当我们使用app自己的用户帐号登录的时候的实现

细细品读 Retrofit 的设计之美 二


细细品读 Retrofit 的设计之美 二


再比如,用微信来登录应用

细细品读 Retrofit 的设计之美 二

细细品读 Retrofit 的设计之美 二


这里我实现了登录产品模块的,其它的模块也是一样的。对于调用者的使用也很简单:

细细品读 Retrofit 的设计之美 二

对于调用者来说很简单,只要关心当前用的是什么平台的帐号系统,而不需要关心具体的实现方式。也把不同平台的登录、注册、获取用户信息等分离开来。当然往往不同的平台可能退出当前帐号的方式是一样,这个时候,其实可以把 BaseLoginer 当做代理对象,目标接口就是 ILoginer,目标对象另外新建一个类实现目标接口,利用代理模式。


Retrofit 抽象工厂的应用

我们都知道网络请求通讯,当服务端返回数据后,都需要进行解析转换为可以直接使用的实体对象,便于设置显示到 UI 界面上,我们在构建 Retrofit 对象的时候往往会给构建器注入一个解析转换器工厂对象。


细细品读 Retrofit 的设计之美 二


其中 FastJsonConverterFactory.create() 创建的就是一个 Factory 抽象工厂对象。

细细品读 Retrofit 的设计之美 二


接下来看看使用 FastJson 作为转换器的工厂实现类:

细细品读 Retrofit 的设计之美 二

通过封装一个 create 方法,来创建工厂对象,外部调用者就不需要关系工厂对象是如何创建的。这点和我上面举的例子是一样的。再一个通过responseBodyConverter、requestBodyConverter 方法分别创建了请求响应和请求发起这两种产品的对象。


再来看看 FastJsonRequestBodyConverter 请求发起转换产品的实现:

细细品读 Retrofit 的设计之美 二

实现了转换器这抽象产品接口类,入参是 RequestBody,返回的结果是泛型T(因为请求的参数是针对具体业务的作为框架无法确定,于是用泛型来代替),这个 FastJsonRequestBodyConverter 产品的功能就是convert 转换功能,这里使用了阿里巴巴的 json 解析库 fastJson 来转换,具体的实现就是通过 JSON.toJSONBytes 方法转换出 json 的字节数组,然后交由给 OkHttp 的 RequestBody.create 来构建一个请求体,并且请求的多媒体类型是 json 格式的。OkHttp 中的实现:


细细品读 Retrofit 的设计之美 二

你会发现 RequestBody 是个抽象类,writeTo 是个抽象方法,那么必定就有调用此方法的地方。也不能盲目的看源码找,一个请求的构建最好的地方就是发起请求的时候,call.enqueue(callback),通过 enqueue 发起一个异步的请求,但 Call 是接口,也不晓得实现类。还有个办法就是倒退的方式,将光标放置上门的 writeTo 方法上,按组合键(有使用到writeTo 的地方):ctrl + alt + F7:


细细品读 Retrofit 的设计之美 二


很明显是最后一个 ReqeustBuilder,请求构建类,跟进去是ContentTypeOverridingRequestBody,它是个代理类,目标对象是其内部的 RequestBody 对象这个对象我们猜测就是上文FastJsonRequestBodyConverter 的 converter 转换创建的RequestBody。再来看看 ContentTypeOverridingRequestBody 在RequestBuild的build() 构建方法中有使用:


细细品读 Retrofit 的设计之美 二


继续看 RequestBuild的build() 的调用者是 ServiceMethod 的toRequest() 方法:

细细品读 Retrofit 的设计之美 二

先看看 apply 方法,它是 ParameterHandler 的抽象方法,里面有很多参数的创建的实现:

细细品读 Retrofit 的设计之美 二


细细品读 Retrofit 的设计之美 二

ServiceMethod的toRequest() 方法调用者是 OkHttpCall 的createRawCall()

细细品读 Retrofit 的设计之美 二


上面的代码意思是,通过一些参数创建了一个请求发起对象,然后再通过一个工厂对象创建了一个用于发起请求的 okhttp3 的 call 对象,再来看看 createRawCall() 方法的调用,它有三个地方调用了:

细细品读 Retrofit 的设计之美 二

很明显我们在发起一个网络业务请求的时候,使用的就是enqueue(callback) 方法,大概来看看具体的实现:

细细品读 Retrofit 的设计之美 二

这样倒过来分析,不知有没有更清晰点,梳理下:


  1. Retrofit 构建的时候,为其设置了 FastJson 的工厂对象。

  2. 上面可知 call.enqueue(callback),call 就是 OkHttpCall 对象。

  3. enqueue 创建的时候会先调 createRawCall

  4. createRawCall 会先调用 serviceMethod 的 toRequest 方法

  5. 在 toRequest方法中,创建 RequestBuild 对象,并且把设置的业务请求的 api 里的参数对象请求体 Body 使用 FastJson 工厂创建的 FastJsonRequestConverter 来 convert 出一个 RequestBody 设置给 RequestBuild 对象,并最终通过构建者模式创建 Request 对象。

  6. 再通过 callFactory 工厂创建一个用于请求的 call,最终交由okhttp 的 enqueue 方法来发起真正的网络请求。


总结

今天的篇幅也比较长,主要说明了抽象工厂设计模式的使用,具体举了个在开发中比较实用的多平台登录的用户系统模块的问题,当然这只是个例子实际项目中需要完善的还很多。通用的例子还有很多比如:多种支付方式的切换、多种地图 SDK 的切换、多种网络框架的切换、多种持久化数据存储方式的切换、多种数据处理方式的切换、多种图片加载器的切换等等。


后面主要介绍了 Retrofit 中抽象工厂的应用,以及简单分析了,Retrofit 是如何构建请求和发起请求的。


篇幅比较长,感谢您的耐心阅读。



与之相关

细细品读 Retrofit 的设计之美一

2017 | 我在 5 个月时间里分享了 98 篇文章



微信号:code-xiaosheng





公众号

「code小生」

细细品读 Retrofit 的设计之美 二