本篇文章已受权为微信公众号 code小生 发布转载请注明出处:www.jianshu.com/p/dab7f5720…java
在上一篇 品读Retrofit设计之美后,咱们了解了Builder构建者模式和(动态)代理模式在Retrofit中的作用,以及它们的使用套路。今天继续品读Retrofit框架值得咱们好好思考的设计:抽象工厂模式设计模式
在看Retrofit的抽象工厂模式的应用前,先来了解下,抽象工厂模式的套路,不扯虚的直接举一个实用的例子:api
咱们都知道做为app开发者,一般的app应用都会有用户系统,一个用户系统每每都包含了如下模块:1. 登陆模块。 2. 注册模块。 3. 找回密码模块。 4. 用户我的信息模块。 这几个模块表明工厂须要生产的不一样类型的产品,用户系统账号,咱们多是app自身的账号、密码、或者手机短信验证码的登陆方式,也多是第三方平台账号登陆:微信、QQ、新浪微博等等。对于不一样的平台的用户账号咱们能够看作不一样的品牌工厂,好比:app自身的用户账号工厂、微信的用户账号工厂、QQ的用户账号工厂、新浪微博的用户账号工厂。数组
这样来设计一个用户系统是否是更清晰点,并且不一样的品牌的工厂便于替换,也就是替换登陆的平台,不一样的产品模块类的功能职责也变的比较单一符合设计模式的单一原则。微信
// ******************IBaseUser.java,抽象用户实体
/** * 抽象用户实体接口,便于泛型化设计 */
public interface IBaseUser {
}
// 1. ******************ILoginer.java,登陆模块
/** * 登陆抽象接口 * @param <U> 用户信息 */
public interface ILoginer<U extends IBaseUser> {
// 登陆
void login(U user);
// 注销、退出账号
void logout(U user);
}
// 2. ******************IRegister.java,注册模块
/** * 注册账号接口 * @param <U> 用户信息 */
public interface IRegister<U extends IBaseUser> {
// 注册账号
void registerAccount(U user);
}
// 3. ******************IFindPwder.java,找回密码模块
/** * 找回密码接口 * @param <U> 用户信息 */
public interface IFindPwder<U extends IBaseUser> {
// 找回密码
void findPwd(U user);
}
// 4. ******************IUserInfoer.java,用户信息模块
/** * 用户信息相关接口 * @param <U> 用户信息 */
public interface IUserInfoer<U extends IBaseUser> {
// 获取用户信息
U getUserInfo();
// 保存用户信息
void saveUserInfo(U userInfo);
}
复制代码
这些产品模块的接口规范功能抽象,对于app的用户系统来讲基本够用了。固然上面的这些接口,也能够统一用一个接口文件来写,这些模块就做为子接口嵌套在里面,这是为了方便管理。网络
// ******************IUserSystemFactory .java,抽象的工厂接口
/** * 用户系统抽象工厂:登陆、注册、找回密码、用户信息等模块 */
public interface IUserSystemFactory {
// 获取登陆模块,登陆器
ILoginer getLoginer();
// 获取注册模块,注册器
IRegister getRegister();
// 找回密码模块
IFindPwder getFindPwder();
// 用户信息模块
IUserInfoer getUserInfoer();
}
复制代码
主要就是获取不一样模块的产品抽象接口对象,便于客户端使用工厂的模块对象的时候多态性。app
由于用户系统大部分状况下都须要和UI交互,因此封装了一层基类把Context上下文统一块儿来,减小子类的没必要要的重复。框架
// *************BaseLoginer.java
/** * 登陆模块的基类 * @param <U> 用户信息 */
public abstract class BaseLoginer<U extends IBaseUser> implements ILoginer<U> {
private Context mContext;
public BaseLoginer(Context context) {
this.mContext = context;
}
}
// *************BaseUserSystemFactory.java
/** * 用户系统工厂基类 */
public abstract class BaseUserSystemFactory implements IUserSystemFactory {
private Context mContext;
public BaseUserSystemFactory(Context context) {
this.mContext = context;
}
// 工厂对象能够获取上下文
public Context getContext(){
return mContext;
}
}
复制代码
好比,当咱们使用app本身的用户账号登陆的时候的实现异步
// ******************SystemAccountLoginer.java
/** * 使用应用账号登陆 */
public class SystemAccountLoginer extends BaseLoginer<User> {
public SystemAccountLoginer(Context context) {
super(context);
}
@Override
public void login(User user) {
// 登陆app
}
@Override
public void logout(User user) {
// 注销退出账号
}
}
// ******************SystemAccountFactory.java
/** * 系统账号登陆时的用户系统工厂 */
public class SystemAccountFactory extends BaseUserSystemFactory {
private SystemAccountFactory(Context context) {
super(context);
}
public static IUserSystemFactory create(Context context){
return new SystemAccountFactory(context);
}
@Override
public ILoginer getLoginer() {
// 返回对应的登陆产品(app本身的账号平台登陆对象)
return new SystemAccountLoginer(getContext());
}
@Override
public IRegister getRegister() {
// 返回对应的注册产品(app本身的账号平台注册对象)
return null;
}
@Override
public IFindPwder getFindPwder() {
// 返回对应的找回密码产品(app本身的账号平台找回密码对象)
return null;
}
@Override
public IUserInfoer getUserInfoer() {
// 返回对应的用户信息产品(app本身的账号平台用户信息对象)
return null;
}
}
复制代码
再好比,用微信来登陆应用
// ******************WeixinLoginer.java
/** * 使用微信登陆 */
public class WeixinLoginer extends BaseLoginer<User> {
public WeixinLoginer(Context context) {
super(context);
}
@Override
public void login(User user) {
// 使用微信登陆
}
@Override
public void logout(User user) {
// 退出登陆
}
}
// ******************WeixinFactory.java
/** * 系统账号登陆时的用户系统工厂 */
public class WeixinFactory extends BaseUserSystemFactory {
private WeixinFactory(Context context) {
super(context);
}
public static IUserSystemFactory create(Context context){
return new WeixinFactory(context);
}
@Override
public ILoginer getLoginer() {
return new WeixinLoginer(getContext());
}
@Override
public IRegister getRegister() {
return null;
}
@Override
public IFindPwder getFindPwder() {
return null;
}
@Override
public IUserInfoer getUserInfoer() {
return null;
}
}
复制代码
这里我实现了登陆产品模块的,其它的模块也是同样的。对于调用者的使用也很简单:
// 客户端调用
// 使用本身的账号平台
IUserSystemFactory factory = SystemAccountFactory.create(this);
// 使用微信平台账号
// IUserSystemFactory weixinFactory = WeixinFactory.create(this);
User user = new User();
user.setUserId("1256339899879");
user.setPhone("13888888888");
// 使用本身的账号登陆app
factory.getLoginer().login(user);
// 使用本身的账号注册
factory.getRegister().registerAccount(user);
// 使用找回本身账号的密码
factory.getFindPwder().findPwd(user);
// 获取用户信息
factory.getUserInfoer().getUserInfo();
复制代码
对于调用者来讲很简单,只要关心当前用的是什么平台的账号系统,而不须要关心具体的实现方式。也把不一样平台的登陆、注册、获取用户信息等分离开来。固然每每不一样的平台可能退出当前账号的方式是同样,这个时候,其实能够把BaseLoginer当作代理对象,目标接口就是ILoginer,目标对象另外新建一个类实现目标接口,利用代理模式。
咱们都知道网络请求通信,当服务端返回数据后,都须要进行解析转换为能够直接使用的实体对象,便于设置显示到UI界面上,咱们在构建Retrofit对象的时候每每会给构建器注入一个解析转换器工厂对象。
new Retrofit.Builder()
.baseUrl(AppConst.BASE_URL)
.client(buildHttpClient())
.addConverterFactory(FastJsonConverterFactory.create())
.build();
复制代码
其中FastJsonConverterFactory.create()建立的就是一个Factory抽象工厂对象。
// 数据转换器抽象产品类
// F是入参,T是出参(转换后的数据类型)
public interface Converter<F, T> {
// 产品的转换操做
T convert(F value) throws IOException;
// 抽象工厂类
abstract class Factory {
// 工厂生产的请求响应的转换器产品
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
// 工厂生产的请求发起的转换器产品
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
// 工厂生产的用于转换字符串数据类型的转换器产品
public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
}
}
复制代码
接下来看看使用FastJson做为转换器的工厂实现类:
public class FastJsonConverterFactory extends Converter.Factory {
// 建立工厂对象
public static FastJsonConverterFactory create() {
return new FastJsonConverterFactory();
}
private FastJsonConverterFactory() {
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return new FastJsonResponseBodyConverter<>(type, mParserConfig, featureValues, features);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new FastJsonRequestBodyConverter<>(serializeConfig, serializerFeatures);
}
}
复制代码
经过封装一个create方法,来建立工厂对象,外部调用者就不须要关系工厂对象是如何建立的。这点和我上面举的例子是同样的。再一个经过responseBodyConverter、requestBodyConverter方法分别建立了请求响应和请求发起这两种产品的对象。
再来看看FastJsonRequestBodyConverter请求发起转换产品的实现:
// 实现了转换器这抽象产品类,入参是RequestBody,返回的结果是泛型T
final class FastJsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private SerializeConfig serializeConfig;
private SerializerFeature[] serializerFeatures;
FastJsonRequestBodyConverter(SerializeConfig config, SerializerFeature... features) {
serializeConfig = config;
serializerFeatures = features;
}
@Override
public RequestBody convert(T value) throws IOException {
byte[] content;
if (serializeConfig != null) {
if (serializerFeatures != null) {
content = JSON.toJSONBytes(value, serializeConfig, serializerFeatures);
} else {
content = JSON.toJSONBytes(value, serializeConfig);
}
} else {
if (serializerFeatures != null) {
content = JSON.toJSONBytes(value, serializerFeatures);
} else {
content = JSON.toJSONBytes(value);
}
}
return RequestBody.create(MEDIA_TYPE, content);
}
}
复制代码
实现了转换器这抽象产品接口类,入参是RequestBody,返回的结果是泛型T(由于请求的参数是针对具体业务的做为框架没法肯定,因而用泛型来代替),这个FastJsonRequestBodyConverter产品的功能就是convert转换功能,这里使用了阿里巴巴的json解析库fastJson来转换,具体的实现就是经过JSON.toJSONBytes方法转换出json的字节数组,而后交由给OkHttp的RequestBody.create来构建一个请求体,而且请求的多媒体类型是json格式的。OkHttp中的实现:
public static RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount) {
if (content == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(content.length, offset, byteCount);
return new RequestBody() {
@Override public MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return byteCount;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
// content请求的参数内容都经过Okio的BufferedSink来写入了
sink.write(content, offset, byteCount);
}
};
}
复制代码
你会发现RequestBody是个抽象类,writeTo是个抽象方法,那么一定就有调用此方法的地方。也不能盲目的看源码找,一个请求的构建最好的地方就是发起请求的时候,call.enqueue(callback),经过enqueue发起一个异步的请求,但Call是接口,也不晓得实现类。还有个办法就是倒退的方式,将光标放置上门的writeTo方法上,按组合键(有使用到writeTo的地方):ctrl + alt + F7:
很明显是最后一个ReqeustBuilder,请求构建类,跟进去是ContentTypeOverridingRequestBody,它是个代理类,目标对象是其内部的RequestBody对象这个对象咱们猜想就是上文FastJsonRequestBodyConverter的converter转换建立的RequestBody。再来看看ContentTypeOverridingRequestBody在RequestBuild的build()构建方法中有使用:
// 很明显由于请求对象初始化比较复杂,就经过构建者模式构建了一个OkHttp的Request对象
class RequestBuild{
Request build() {
// 很明显咱们在构建Retrofit的时候有传入FastJson的请求发起产品的生成工厂对象,所以姑且任务body是有值的不等于null
RequestBody body = this.body;
if (body == null) {
// Try to pull from one of the builders.
if (formBuilder != null) {
body = formBuilder.build();
} else if (multipartBuilder != null) {
body = multipartBuilder.build();
} else if (hasBody) {
// Body is absent, make an empty body.
body = RequestBody.create(null, new byte[0]);
}
}
// 这里给body作了一层代理,实际的目标接口仍是以前FastJsonRequestBodyConverter建立的body目标对象本身来调用的
// 然后把代理对象body给了Request进行构建请求发起对象。
MediaType contentType = this.contentType;
if (contentType != null) {
if (body != null) {
body = new ContentTypeOverridingRequestBody(body, contentType);
} else {
requestBuilder.addHeader("Content-Type", contentType.toString());
}
}
// 这里又经过OkHttp的Request类自身的构建者最终建立了Request对象
return requestBuilder
.url(url)
.method(method, body)
.build();
}
}
复制代码
继续看RequestBuild的build()的调用者是ServiceMethod的toRequest()方法:
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
// ....省略代码
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
复制代码
先看看apply方法,它是ParameterHandler的抽象方法,里面有不少参数的建立的实现:
@Override void apply(RequestBuilder builder, T value) {
if (value == null) {
throw new IllegalArgumentException("Body parameter value must not be null.");
}
RequestBody body;
try {
// 调用的这个convert这个方法就是上面fastjson工厂转换建立请求发起RequestBody对象的调用处
body = converter.convert(value);
} catch (IOException e) {
throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
}
// 这里把建立的RequestBody对象设置给了RequestBuild构建者。这就是构建者的好处(初始化一个Request对象不容易,属性的初始化时机和位置有各类状况)
builder.setBody(body);
}
复制代码
ServiceMethod的toRequest()方法调用者是OkHttpCall的createRawCall()
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
复制代码
上面的代码意思是,经过一些参数建立了一个请求发起对象,而后再经过一个工厂对象建立了一个用于发起请求的okhttp3的call对象,再来看看createRawCall()方法的调用,它有三个地方调用了:
// 一个同步的请求方法
public synchronized Request request() {}
// 异步的请求方法,可是没有请求回调
public Response<T> execute() throws IOException {}
// 异步的请求方法,有请求回调接口对象处理
public void enqueue(final Callback<T> callback) {}
复制代码
很明显咱们在发起一个网络业务请求的时候,使用的就是enqueue(callback)方法,大概来看看具体的实现:
@Override public void enqueue(final Callback<T> callback) {
// 这里请求回调若是是null,直接就报空指针异常,这点在开发的时候须要作好非空判断处理
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure; // 建立的时候的有可能有错
if (call == null && failure == null) {
try {
// 初次构建使用的时候,会去建立一个call
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
// 出错则回调请求失败
callback.onFailure(this, failure);
return;
}
if (canceled) {
// 请求若有取消,则取消
call.cancel();
}
// 此处才是,真正发起请求的地方,把请求交由给底层OkHttp来作。
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException {
Response<T> response;
try {
// 请求成功返回后,解析响应
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
// 告知回调请求成功
callSuccess(response);
}
// 请求失败
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
// 回调给业务请求调用处,告知请求成功
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
复制代码
这样倒过来分析,不知有没有更清晰点,梳理下:
Retrofit构建的时候,为其设置了FastJson的工厂对象。
上面可知call.enqueue(callback),call就是OkHttpCall对象。
enqueue建立的时候会先调createRawCall
createRawCall会先调用serviceMethod的toRequest方法
在toRequest方法中,建立RequestBuild对象,而且把设置的业务请求的api里的参数对象请求体Body使用FastJson工厂建立的FastJsonRequestConverter来convert出一个RequestBody设置给RequestBuild对象,并最终经过构建者模式建立Request对象。
再经过callFactory工厂建立一个用于请求的call,最终交由okhttp的enqueue方法来发起真正的网络请求。
今天的篇幅也比较长,主要说明了抽象工厂设计模式的使用,具体举了个在开发中比较实用的多平台登陆的用户系统模块的问题,固然这只是个例子实际项目中须要完善的还不少。通用的例子还有不少好比:多种支付方式的切换、多种地图SDK的切换、多种网络框架的切换、多种持久化数据存储方式的切换、多种数据处理方式的切换、多种图片加载器的切换等等。
后面主要介绍了Retrofit中抽象工厂的应用,以及简单分析了,Retrofit是如何构建请求和发起请求的。
我是JerryloveEmily,感谢您的阅读,
喜欢就点个赞呗,“❤喜欢”,
鼓励又不花钱,您在看,我就继续写~
非简书用户,能够点右上角的三个“...”,而后"在Safari中打开”,就能够点赞咯~