示例
OKHttp网络请求:
FormBody formBody = new FormBody.Builder()
.add("pay_fee", String.valueOf(product.getProductPrice()))
.add("product_name", String.valueOf(product.getProductName()))
.add("access_token", "")
.build();
Request request = new Request.Builder()
.post(formBody)
.url(createOrderURL)
.build();
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
call.enqueue(null);
Glide的图片加载:
Glide.with(this)
.load(sampledPhotoPath)
.asBitmap()
.format(DecodeFormat.ALWAYS_ARGB_8888)
.skipMemoryCache(true)
.transform(new FilterEffectBitmapTransformation(this,
mFilterInfo, sampledPhotoPath))
.into(mBinding.appBeautyFilterMarkPhotoIv);
调用上面的代码的感觉“爽”,接口简单,方便的网络请求,方便的图片加载。从上面的代码代码看出接口的定义非常易用。我们该怎么写出如此简单易用的接口?下面我们一起探讨一下。
从上面代码可以看出配置的时候的使用了.的一步步的连接起来,其实这种方式采用了Builder模式,使得接入的时候非常方便,而且代码很清晰。
OKHttp结构图:
OKHttp的接口
- 对外接口
- OkHttpClient, Builder用于全局的请求配置和作为Call的工厂方法
- Request,Builder, RequestBody用于构建请求数据
- Call可执行的网络请求
- 底层接口执行网络请求,比如Dispather请求分发,Interceptor,Chain等类被上层接口封装,不暴露给client。
Glide的结构图:
Glide的接口:
- 对外接口:
- Glide,RequestManager,GenericRequestBuilder封装了底层接口
- 底层接口是实际的图片加载。基本不暴露给client使用,除非涉及到了相关的配置。
其实上面的对外接口的设计用到了一种设计模式Facade模式。此模式封装子系统,向外提供了更高层的接口主要有以下几个好处:
- 提高了对外接口的易用性,降低了接入成本
- 子系统的不想暴露的接口可以隐藏,更好的体现了面向对象的思想。
- 子系统做兼容,或者是代码重构,亦或是流程修改,只要高层接口不改变,不会影响到client。
从上面我们得出了提高对外接口的易用性的常用的设计方式
- Builder模式用于配置
- Facade模式封装子系统来提供对外的高层接口
下面我们就简单介绍一下如何使用这两种设计模式。
设计模式
Builder模式
基础知识,可参考 刘伟的博客。
这个模式在对外接口设计中一般用于配置或者构建复杂对象。
比如OKHttp里的配置:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.build();
为了简化这里只是列举了部分信息。
public static final class Builder {
int connectTimeout;
int readTimeout;
public Builder() {
connectTimeout = 10_000;
readTimeout = 10_000;
}
Builder(OkHttpClient okHttpClient) {
this.connectTimeout = okHttpClient.connectTimeout;
this.readTimeout = okHttpClient.readTimeout;
}
public Builder connectTimeout(long timeout, TimeUnit unit) {
connectTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public Builder readTimeout(long timeout, TimeUnit unit) {
readTimeout = checkDuration("timeout", timeout, unit);
return this;
}
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final int connectTimeout;
final int readTimeout;
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
}
public int connectTimeoutMillis() {
return connectTimeout;
}
public int readTimeoutMillis() {
return readTimeout;
}
}
- Builder构建OkHttpClient的时候,客户端可以配置信息,然后调用build就可以构建成功。
- Builder构建OKHttpClient的时候,readTimeout接口或者connectTimeout接口返回Builder对象,这样的客户端调用的时候只需要用.来连接相关的方法,更加方便。
大家也可以查看GenericRequestBuilder如何构建Request的,或者Android的Intent的类,来了解Builder的使用。
实现的技巧:
- 实现的时候必要参数可以在构造函数中传递,其他的使用接口设置
- Builder的类返回的函数可以返回当前的对象,以此可以使用调用的.的符号记性连接。
Facade设计模式
为子系统的一组接口提供一个一致的界面,Facade模式定义了一些高层接口,这些接口使得子系统更加容易使用。
下面以我以前做的游戏联运的SDK为例。
对外提供接口这里使用了Facade模式,
- MTOnlieGameSDK为主要的对外的高层接口类。
- MTOnlieGameSDK封装了其他子系统,MTAccountManager,MTPayManager
- 同时MTAccountManager,MTPayManager也使用了Facade模式,MTAccountManager封装帐号SDK,MTPayManager封装了与支付APK的通信。
Notice:
- 封装了子系统对外的接口得到简化,提高了对外接口的易用性。
- 使用Facade模式封装更高层的接口,避免了不必要的接口的暴露,同时子系统与客户端代码分离,提高了代码的独立性。比如更改MTAccountManager的实现不会影响到client的代码修改。
此外在层次结构接口中引入Facade模式封装每个层级,可以简化层级之间的依赖。
其他问题
- 接口的暴露,不需要的接口尽量不暴露用户。
- 参数的数量的问题,一方面尽量减少参数,一般参数3个左右为宜,多了可以使用封装成类进行传递。
- 参数依赖抽象类,比如Activity和Context的使用,能使用Context尽量不要使用Activity等等。也就是尽量使用接口而不是具体类。
- 工厂方法或者抽象工厂来创建对象。比如OKHttp的Call.Factory。
注意:
- 设计模式的使用只是为了得到好的代码设计,而过度使用会造成冗余,三思再用。
- 设计模式的使用不要过于依赖所谓的类图或者结构图,往往稍微变化的设计模式更好用。
参考资料
- 设计模式-可复用面向对象软件的基础 只服四人帮的这本书,虽然例子和内容过时了,但设计思想杠杠的。
- LoveLion的博客
`