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

设计模式-装饰者模式

作者: 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流了。

总结

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

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

相关文章

  • 设计模式

    设计模式 单例模式、装饰者模式、

  • 设计模式笔记汇总

    目录 设计原则 “依赖倒置”原则 未完待续... 设计模式 设计模式——策略模式 设计模式——装饰者模式 设计模式...

  • java IO 的知识总结

    装饰者模式 因为java的IO是基于装饰者模式设计的,所以要了解掌握IO 必须要先清楚什么事装饰者模式(装饰者模式...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计

    JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式 装饰器模式,又名装饰者模式。它的定义是“在不...

  • 8种设计模式:

    主要介绍 单例设计模式,代理设计模式,观察者设计模式,模板模式(Template), 适配器模式,装饰模式(Dec...

  • 装饰者模式

    JavaScript 设计模式 张容铭第十二章 房子装修--装饰者模式 (102页) 装饰者模式(Decorato...

  • Summary of February 2017

    READING Head First 设计模式:完成50%。内容:观察者模式、装饰者模式、工厂模式、单件模式、命令...

  • 装饰对象:装饰者模式

    装饰对象:装饰者模式   这是《Head First设计模式(中文版)》第三章的读书笔记。   装饰者模式,可以称...

  • 设计模式之装饰器模式

    也称装饰者模式、装饰器模式、Wrapper、Decorator。 装饰模式是一种结构型设计模式,允许你通过将对象放...

网友评论

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

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