美文网首页设计模式
设计模式-装饰者模式

设计模式-装饰者模式

作者: h2coder | 来源:发表于2020-10-16 00:29 被阅读0次

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

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

    怎么实现装饰者模式?

    • 抽象组件,接口或抽象类,是被装饰对象和装饰器共同的父类或实现的接口。

    • 具体组件,被装饰的的对象。

    • 抽象装饰类,一般为抽象类,继承了抽象组件或实现了抽象组件。持有一个被装饰对象的引用,复写抽象组件的方法,在调用被装饰对象前后做装饰处理。

    • 具体装饰类,抽象装饰类的实现类。

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

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

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

    使用步骤

    1. 基本对象
    • 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);
    }
    
    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,添加公共参数。
    • 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);
            }
        }
    
    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流了。

    总结

    • 优点:装饰者模式通过组合的方式拓展类的行为,不需要继承。

    • 缺点:设计模式通病,会增加比较多子类的类文件个数。

    相关文章

      网友评论

        本文标题:设计模式-装饰者模式

        本文链接:https://www.haomeiwen.com/subject/yfompktx.html