zoukankan      html  css  js  c++  java
  • 【趣味设计模式系列】之【装饰器模式】

    1. 简介

    装饰器模式(Decorator Pattern):动态地给一个对象添加职责,就增加功能来说,装饰器比生成子类更灵活。

    2. 示例

    水果店需要给网上客户发货,除了包装之外,需要对特定水果包装加额外装饰,比如加防伪标志、加固、加急等额外功能,但在外部看来还是打包组件。

    类图设计

    水果包装接口类Bag,接口方法pack,完成水果打包。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:13
     * @Desc: 包装接口
     */
    public interface Bag {
        void pack();
    }
    
    

    苹果、橘子、香蕉各自实现包装接口,用自己的特定的外包装。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:15
     * @Desc: 苹果包装类
     */
    public class AppleBag implements Bag {
        @Override
        public void pack() {
            System.out.println("苹果使用纸箱包装");
        }
    }
    
    
    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:15
     * @Desc: 橘子包装类
     */
    public class OrangeBag implements Bag {
        @Override
        public void pack() {
            System.out.println("橘子使用网兜包装");
        }
    }
    
    
    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:15
     * @Desc: 香蕉包装类
     */
    public class BananaBag implements Bag {
        @Override
        public void pack() {
            System.out.println("香蕉使用竹箩包装");
        }
    }
    
    

    装饰器类BagDecorator,实现包装类Bag接口,同时拥有包装接口的引用,为了组装更多具体装饰器加入进来,增加包装类的装饰功能。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:23
     * @Desc: 装饰器类
     */
    public class BagDecorator implements Bag {
        private Bag bag;  //维持一个对抽象构件对象的引用
    
        public BagDecorator(Bag bag)  //注入一个抽象构件类型的对象
        {
            this.bag = bag;
        }
    
        public void pack() {
            bag.pack();
        }
    }
    
    

    防伪装饰器CheckedBagDecorator,继承BagDecorator类,并增加自己的防伪标识方法checked。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:32
     * @Desc: 防伪装饰器
     */
    public class CheckedBagDecorator extends BagDecorator {
        public CheckedBagDecorator(Bag bag) {
            super(bag);
        }
    
        @Override
        public void pack() {
            super.pack();
            checked();  //打印防伪标识
        }
    
        //增加防伪标识
        public void checked() {
            System.out.println("===============");
            System.out.println("打印上防伪标识");
        }
    }
    
    

    加固装饰器ReinforceBagDecorator,继承BagDecorator类,并增加自己的加固方法reinforce。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:34
     * @Desc: 加固装饰器
     */
    public class ReinforceBagDecorator extends BagDecorator{
        public ReinforceBagDecorator(Bag bag) {
            super(bag);
        }
    
        public void pack() {
            super.pack();  //调用原有业务方法
            reinforce();
        }
    
        //加固包装
        public void reinforce() {
            System.out.println("===============");
            System.out.println("加固了包装");
        }
    }
    
    

    加急装饰器SpeedBagDecorator,继承BagDecorator类,并增加自己的加急方法speedy。

    package com.wzj.decorator;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:34
     * @Desc: 加急装饰器
     */
    public class SpeedBagDecorator extends BagDecorator {
    
        public SpeedBagDecorator(Bag bag) {
            super(bag);
        }
    
        public void pack() {
            super.pack();  //调用原有业务方法
            speedy();
        }
    
        //快件加急
        public void speedy() {
            System.out.println("===============");
            System.out.println("打上加急标识");
        }
    }
    
    

    客户端类

    package com.wzj.decorator;
    
    import org.aspectj.weaver.ast.Or;
    
    /**
     * @Author: wzj
     * @Date: 2020/9/8 10:40
     * @Desc:
     */
    public class Client {
        public static void main(String[] args) {
            AppleBag appleBag = new AppleBag();
            OrangeBag orangeBag = new OrangeBag();
            BananaBag bananaBag = new BananaBag();
            // 苹果纸箱包装后,外加防伪标识、加固包装
            new ReinforceBagDecorator(new CheckedBagDecorator(appleBag)).pack();
            System.out.println("*********************************");
            // 橘子网兜包装后,外加防伪标识、加固、加急包装
            new SpeedBagDecorator(new ReinforceBagDecorator(new CheckedBagDecorator(orangeBag))).pack();
    
        }
    }
    
    

    结果

    苹果使用纸箱包装
    ===============
    打印上防伪标识
    ===============
    加固了包装
    *********************************
    橘子使用网兜包装
    ===============
    打印上防伪标识
    ===============
    加固了包装
    ===============
    打上加急标识
    

    从上述例子可以看出,装饰器的好处,不仅可以对具体的水果包装类进行装饰,多个装饰器还可以嵌套装饰,非常灵活,这也是为什么,装饰器中需要引用Bag类,就是方便嵌套,因为每个具体的装饰器,本身也是Bag的子类。

    3. 源码分析

    Java IO类库非常庞大,从大类分,如果从按流的方向来分的话,分为输入流InputStream,输出流OutputStream,如果按照读取的方式分的话,分为字节流与字符流,具体如下图

    针对不同的读取和写入场景,Java IO 又在这四个父类基础之上,扩展出了很多子类,如下图

    OutputStream 是一个抽象类,FileOutputStream 是专门用来写文件流的子类,FilterOutputStream很特殊,它实现了OutputStream,同时持有OutputStream的引用,部分源码如下图:

    public class FilterInputStream extends InputStream {
        /**
         * The input stream to be filtered.
         */
        protected volatile InputStream in;
    
        ...
    

    所以FilterOutputStream在设计的时候本质就是一个装饰器,其子类BufferedOutputStream,DataOutputStream,PrintStream都是具体的装饰器,实现额外的功能。
    同样FilterInputStream、InputStreamReader、OutputStreamWriter都是装饰器类,其子类充当具体装饰的功能。
    在平时写代码的时候都有如下几行嵌套的写法:

    File f = new File("c:/work/test.data");
    FileOutputStream fos = new FileOutputStream(f);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    BufferedWriter bw = new BufferedWriter(osw);
    bw.write("https://githup.com");
    bw.flush();
    bw.close();
    

    本质上,OutputStream的实现类可以与具体装饰器实现相互嵌套,具体的装饰器之间也可以相互嵌套,非常灵活,避免了独立为每个类创建子类而产生累爆炸的不合理设计。

    4. 总结

    4.1 优点

    • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
    • 处理可以撤销的职责。
    • 扩展子类灵活,避免产生累爆炸。

    4.2 缺点

    • 如果最里面的装饰器出错,需要从外面一层一层往里面去查看,多层嵌套导致增加复杂性。
  • 相关阅读:
    Java实现分页
    研发技能列表
    shell 函数
    养生
    再谈创新
    写代码注意事项
    排查问题方法
    简历撰写
    jenkins
    架构
  • 原文地址:https://www.cnblogs.com/father-of-little-pig/p/13622216.html
Copyright © 2011-2022 走看看