什么是装饰者模式?什么时候用?
装饰者模式,可以动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。平时我们拓展类,一般为继承,但是继承强制将唯一继承的位置占用了,如果需要继承别的类时则不能继承了。而装饰者模式是使用组合来拓展类,平时我们应该多用组合少用继承。
怎么实现装饰者模式?
-
抽象组件,接口或抽象类,是被装饰对象和装饰器共同的父类或实现的接口。
-
具体组件,被装饰的的对象。
-
抽象装饰类,一般为抽象类,继承了抽象组件或实现了抽象组件。持有一个被装饰对象的引用,复写抽象组件的方法,在调用被装饰对象前后做装饰处理。
-
具体装饰类,抽象装饰类的实现类。
装饰者模式优雅添加公共参数、打印请求参数、响应参数
平时我们在封装请求框架时,为了开发方便,一般会在请求前打印请求参数,请求后打印响应参数。一开始我们可能直接在具体的请求、响应代码中加,但是就会容易造成臃肿。而且如果需要替换别的请求框架时,这些打印代码会进行拷贝,需要修改时就会比较多地方修改。
那怎么解决呢?其实我们可以使用装饰着模式,将请求操作抽取为操作接口,具体的请求框架都新建一个实现类,在实现类中调用框架的请求方法,增加一个装饰器类,同样实现Api接口,将实现类注入到装饰器中,在每个接口方法中,调用被装饰对象前后打印Log,再以装饰器对象作为使用,使用方调用方法即可。
使用步骤
- 基本对象
- ApiParams,请求参数封装
public class ApiParams {
private boolean hasFile;
private Map<String, ArrayList<String>> params;
private Map<String, ArrayList<File>> files;
//...省略其他add方法
public synchronized ApiParams add(ApiParams apiParams) {
//...
return this;
}
/**
* 添加请求参数
*/
public synchronized ApiParams add(String key, String value) {
//...
return this;
}
/**
* 添加多个请求参数
*/
public synchronized ApiParams add(String key, ArrayList<String> values) {
//...
return this;
}
/**
* 添加文件
*/
public synchronized ApiParams addFile(String key, File file) {
//...
return this;
}
/**
* 添加多个文件
*/
public synchronized ApiParams addFile(String key, Collection<File> files) {
//...
return this;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
//...拼接打印参数
return builder.toString();
}
}
- ApiCallback,请求回调接口
public interface ApiCallback {
/**
* 接口执行前回调
*
* @param tag 请求tag,用于区分请求那个接口
*/
void onApiStart(String tag);
/**
* 接口请求成功时回调
*
* @param result 接口返回的结果
* @param tag 请求tag,用于区分请求那个接口
*/
void onApiSuccess(String result, String tag);
/**
* 接口请求网络出错时回调
*
* @param throwable 错误
* @param strMsg 错误信息
* @param tag 请求tag,用于区分请求那个接口
*/
void onApiFailure(Throwable throwable, String strMsg, String tag);
/**
* 接口返回的结构解析为对象出错时回调
*
* @param tag 请求tag,用于区分请求那个接口
*/
void onParseError(String tag);
}
- 抽象组件,这里就是我们的操作接口。分为同步和异步2种,平时用得比较多的就是get和post请求,所以暂时只提供了这2个。
public interface Api {
/**
* 同步get
*
* @param url 请求地址
* @param params 请求参数
*/
String getSync(String url, ApiParams params) throws Exception;
/**
* 异步get
*
* @param callback 回调实例对象
* @param url 请求地址
* @param params 请求参数
* @param tag 请求的方法名
*/
void get(final ApiCallback callback, final String url, final ApiParams params, final String tag);
/**
* 同步post
*
* @param url 请求地址
* @param params 请求参数
*/
String postSync(String url, ApiParams params) throws Exception;
/**
* 异步post
*
* @param callback 回调实例
* @param url 请求地址
* @param params 请求参数
* @param tag 调用的方法名
*/
void post(final ApiCallback callback, final String url, final ApiParams params, final String tag);
}
- 具体组件。就是请求框架的具体实现类。这里以Okhttp3为例子(实现不是我们的目的,省略实现)。所以也可以很方便的拓展实现为Volly。
public class ApiByOkHttp implements Api {
@Override
public String getSync(String url, ApiParams params) throws Exception {
//...
}
@Override
public void get(final ApiCallback callback, String url, ApiParams params, final String tag) {
//...
}
@Override
public String postSync(String url, ApiParams params) throws Exception {
//...
}
@Override
public void post(final ApiCallback callback, String url, ApiParams params, final String tag) {
//...
}
}
- 抽象装饰类和具体装饰类,这里我们比较简单,没有进行抽象,直接是装饰类。
装饰器类:- LoggingApiDecorator,同步请求打印Log。
- LoggingApiCallback,异步请求打印Log。
- CommonParamsApiDecorator,添加公共参数。
- LoggingApiDecorator
public static class LoggingApiDecorator implements Api {
private Api apiImpl;
private LogPrinter logPrinter;
private final Handler mainHandler;
public LoggingApiDecorator(Api apiImpl, LogPrinter logPrinter) {
this.apiImpl = apiImpl;
this.logPrinter = logPrinter;
this.mainHandler = new Handler(Looper.getMainLooper());
}
private void printRequest(String url, ApiParams params) {
logPrinter.printRequest(url, params);
}
private void printResult(String json) {
logPrinter.printResult(json);
}
@Override
public String getSync(String url, ApiParams params) throws Exception {
//请求前打印请求参数
printRequest(url, params);
String json = apiImpl.getSync(url, params);
printResult(json);
return json;
}
@Override
public void get(ApiCallback callback, String url, ApiParams params, String tag) {
printRequest(url, params);
apiImpl.get(new LoggingApiCallback(mainHandler, callback, logPrinter), url, params, tag);
}
@Override
public String postSync(String url, ApiParams params) throws Exception {
//请求前打印请求参数
printRequest(url, params);
String json = apiImpl.postSync(url, params);
//请求后打印响应
printResult(json);
return json;
}
@Override
public void post(ApiCallback callback, String url, ApiParams params, String tag) {
printRequest(url, params);
apiImpl.post(new LoggingApiCallback(mainHandler, callback, logPrinter), url, params, tag);
}
}
- LoggingApiCallback
public static class LoggingApiCallback implements ApiCallback {
private Handler mainHandler;
private ApiCallback originCallback;
private LogPrinter logPrinter;
public LoggingApiCallback(Handler mainHandler, ApiCallback originCallback, LogPrinter logPrinter) {
this.mainHandler = mainHandler;
this.originCallback = originCallback;
this.logPrinter = logPrinter;
}
@Override
public void onApiStart(final String tag) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (originCallback != null) {
originCallback.onApiStart(tag);
}
}
};
ensureMainThread(runnable);
}
@Override
public void onApiFailure(final Throwable throwable, final String strMsg, final String tag) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (originCallback != null) {
originCallback.onApiFailure(throwable, strMsg, tag);
}
}
};
ensureMainThread(runnable);
}
@Override
public void onParseError(final String tag) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (originCallback != null) {
originCallback.onParseError(tag);
}
}
};
ensureMainThread(runnable);
}
@Override
public void onApiSuccess(final String json, final String tag) {
//打印结果
logPrinter.printResult(json);
Runnable runnable = new Runnable() {
@Override
public void run() {
if (originCallback != null) {
originCallback.onApiSuccess(json, tag);
}
}
};
ensureMainThread(runnable);
}
//保证主线程回调
private void ensureMainThread(Runnable runnable) {
if (HttpUtil.isUIThread()) {
runnable.run();
} else {
mainHandler.post(runnable);
}
}
}
- CommonParamsApiDecorator
public static class CommonParamsApiDecorator implements Api {
private Api apiImpl;
private ApiParams commonParams;
/**
* 添加公共参数
*/
public CommonParamsApiDecorator(Api apiImpl, ApiParams commonParams) {
this.apiImpl = apiImpl;
this.commonParams = commonParams;
}
private void addCommonParams(ApiParams params) {
params.add(commonParams);
}
@Override
public String getSync(String url, ApiParams params) throws Exception {
//请求前,统一加通用参数,后面的方法也一样,就不写注释了
addCommonParams(params);
return apiImpl.getSync(url, params);
}
@Override
public void get(ApiCallback callback, String url, ApiParams params, String tag) {
addCommonParams(params);
apiImpl.get(callback, url, params, tag);
}
@Override
public String postSync(String url, ApiParams params) throws Exception {
addCommonParams(params);
return apiImpl.postSync(url, params);
}
@Override
public void post(ApiCallback callback, String url, ApiParams params, String tag) {
addCommonParams(params);
apiImpl.post(callback, url, params, tag);
}
}
- 配置装饰
public HttpRequester(Api apiImpl, LogPrinter logPrinter) {
//装饰,打印Log
LoggingApiDecorator loggingApiDecorator = new LoggingApiDecorator(apiImpl, logPrinter);
//装饰,增加公共参数和打印参数,一定要在最后包装
ApiParams commonParams = new ApiParams();
commonParams.add("token", "xxx123yy");
this.api = new CommonParamsApiDecorator(loggingApiDecorator, commonParams);
}
- 打印结果
LogUtils: AppContext$2.printRequest(AppContext.java:58): token = xxx123yy
AppContext$2.printResult(AppContext.java:65): {
"error": false,
"results": [
{
"_id": "5c6a4ae99d212226776d3256",
"createdAt": "2019-02-18T06:04:25.571Z",
"desc": "2019-02-18",
"publishedAt": "2019-02-18T06:05:41.975Z",
"source": "web",
"type": "福利",
"url": "https:\/\/ws1.sinaimg.cn\/large\/0065oQSqly1g0ajj4h6ndj30sg11xdmj.jpg",
"used": true,
"who": "lijinshanmx"
},
...
Java中的装饰器模式
Java中我们最常见的装饰器事件就是IO流了。
总结
-
优点:装饰者模式通过组合的方式拓展类的行为,不需要继承。
-
缺点:设计模式通病,会增加比较多子类的类文件个数。
网友评论