设计模式

设计模式-装饰者模式

2020-10-16  本文已影响0人  h2coder

什么是装饰者模式?什么时候用?

装饰者模式,可以动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。平时我们拓展类,一般为继承,但是继承强制将唯一继承的位置占用了,如果需要继承别的类时则不能继承了。而装饰者模式是使用组合来拓展类,平时我们应该多用组合少用继承。

怎么实现装饰者模式?

装饰者模式优雅添加公共参数、打印请求参数、响应参数

平时我们在封装请求框架时,为了开发方便,一般会在请求前打印请求参数,请求后打印响应参数。一开始我们可能直接在具体的请求、响应代码中加,但是就会容易造成臃肿。而且如果需要替换别的请求框架时,这些打印代码会进行拷贝,需要修改时就会比较多地方修改。

那怎么解决呢?其实我们可以使用装饰着模式,将请求操作抽取为操作接口,具体的请求框架都新建一个实现类,在实现类中调用框架的请求方法,增加一个装饰器类,同样实现Api接口,将实现类注入到装饰器中,在每个接口方法中,调用被装饰对象前后打印Log,再以装饰器对象作为使用,使用方调用方法即可。

使用步骤

  1. 基本对象
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();
    }
}
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);
}
  1. 抽象组件,这里就是我们的操作接口。分为同步和异步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);
}
  1. 具体组件。就是请求框架的具体实现类。这里以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) {
        //...
    }
}
  1. 抽象装饰类和具体装饰类,这里我们比较简单,没有进行抽象,直接是装饰类。
    装饰器类:
    • LoggingApiDecorator,同步请求打印Log。
    • LoggingApiCallback,异步请求打印Log。
    • CommonParamsApiDecorator,添加公共参数。
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);
    }
}
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);
        }
    }
}
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);
        }
    }
  1. 配置装饰
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);
}
  1. 打印结果
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流了。

总结

上一篇 下一篇

猜你喜欢

热点阅读