一、基本概念
1.1 定义
要求一个子系统的外部与其内部的通信必须通过一个 统一的对象 进行,外观模式提供一个高层次的接口,使得子系统更易于使用。即使具体的子系统发生了变化,用户也不会感知到。
1.2 例子
外观模式外观模式接口比较简单,就是通过一个统一的接口对外提供服务,使得外部程序只 通过一个类就可以实现系统内部的多种功能,而这些实现功能的内部子系统之间可能也有交互,通过外观类来屏蔽这些复杂的交互,降低用户的使用成本。
由于这种模式比较简单,不容易与其它的模式混淆,这里就不举简单的例子了。
1.3 应用场景
- 为一个复杂子系统提供一个简单接口。
- 当你需要一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。
1.4 优缺点
优点
- 对客户程序隐藏子系统细节,因而减少了客户对于子系统的耦合。
- 外观类对子系统的接口封装,使得系统更易于使用。
缺点
- 外观类接口膨胀,由于子系统的接口都由外观类统一对外暴露,使得外观类的
API
接口较多,在一定程度上增加了用户的使用成本。 - 外观类没有遵循开闭原则,当业务发生变更时,可能需要直接修改外观类。
二、Android 源码
当我们启动一个新页面的时候,只需要调用startActivity
方法,如 Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通信实现 一文中分析的,在其背后做了很多调用者不可见的工作:
- 当我们调用
startActivity
,其实是通过其基类ContextWrapper
中的mBase
对象来实现的,它的类型为ContextImpl
。 - 在
ContextImpl
又调用了Instrumentation
的execStartActivity
方法,它再通过应用侧的ActivityManagerProxy
和ActivityManagerService
完成Binder
通信,向管理者发送信息。 - 管理者再通过
ApplicationThreadProxy
向应用侧发送回执消息。
这其实就是外观模式的运用,使用者不必关心里面的具体实现,它只需要将启动的意图信息放在Intent
当中,之后就交给系统自己去处理了。如果在之后的版本中,需要修改行为,也不会影响到调用者。
三、项目运用
之前在项目当中直接使用OKHttp
的时候,由于没有封装,缓存也是由业务自己去处理,非常混乱,如果要实现一个带缓存的请求很麻烦。从业务层面来说,使用第三方框架时,有几个痛点无法满足,但是又是很多业务需要用到的:
- 缓存处理
- 第一次进入无网,需要配置默认数据
- 返回数据的
JSON
解析,默认数据的读取,都避免放在耗时的主线程中 - 回调结果的线程可配置
- 请求日志的统一处理
- 直接使用第三方框架侵入性太大,对于业务来说,只关心请求和返回,不应当与具体的网络框架耦合
之后就对网络请求做了统一的封装,这其实就是外观模式的典型运用。
在发起请求的时候,只需要构造一个
NetRequest
对象,在该对象中可以进行各种参数的配置。
public class NetRequest {
private long expireTime;
private String url;
private Map<String, String> queryMaps = new HashMap<>();
private Map<String, String> headerMaps = new HashMap<>();
private ResponseProcessor responseProcessor;
private @CacheType
int cacheType;
private boolean dealOnUiThread;
private @RequestType
int requestType;
private @ContentType
String contentType;
private @Encoding
String encoding = Encoding.UTF_8;
private String paramJson;
private String paramForm;
private Map<String, String> params = new HashMap<>();
private boolean noCache;
private List<INetCallback> netCallbacks;
private String cacheKey;
private IProgressCallback progressCallback;
private boolean filterEqualBody;
//...
}
发起网络请求的时候,只需要调用NetExecutor#doAsync(NetRequest netRequest)
方法传入配置好的请求对象就可以了,在内部会去解析NetRequest
当中的参数,完成最终的网络请求,并将结果封装成NetResponse
返回,加入我们后面需要新增功能,那么只需要修改系统内部的实现就可以了。
public class NetHttpImpl extends INetHttp {
public NetHttpImpl(NetExecutor netExecutor) {
super(netExecutor);
}
@Override
public <T> NetResponse<T> doRealHttpRequest(NetRequest netRequest, NetResponse<T> cacheResponse) {
NetResponse<T> netResponse;
try {
//添加拦截器监听。
addInterceptorListener(netRequest);
//创建 OkHttp 的 Request。
final Request trueHttpRequest = createHttpRequest(netRequest);
Call call = netExecutor.getOkHttpClient().newCall(trueHttpRequest);
okhttp3.Response httpResponse = call.execute();
//正常情况下,将 OkHttp 的 Response 转换为调用者看到的 NetResponse。
netResponse = dealHttpResponse(netRequest, httpResponse);
} catch (Exception e) {
//异常情况下,将 OkHttp 的 Response 转换为调用者看到的 NetResponse。
netResponse = dealHttpException(e);
} finally {
//取消拦截器的监听。
removeInterceptorListener(netRequest);
}
return netResponse;
}
/**
* 通过请求配置创建 OkHttp 的 request。
* @param netRequest 请求配置。
* @return OkHttp 的请求。
*/
private okhttp3.Request createHttpRequest(NetRequest netRequest) {
okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder();
//1.添加 url。
addHttpRequestUrl(netRequest, requestBuilder);
//2.添加请求header 参数。
addHttpRequestHeader(netRequest, requestBuilder);
//3.创建请求的 body。
addHttpRequestBody(netRequest, requestBuilder);
//4.创建请求。
return requestBuilder.build();
}
}
四、参考文献
- <<
Android
源码设计模式 - 解析与实战>>
网友评论