从IO中学习装饰器模式

最近在重学IO,我们在创建输入输出流的时候总会在构造函数中嵌套很多类,比如:BufferedReader in = new BufferedReader(new FileReader(fileName));,其实这段代码就是装饰器模式的应用,那什么是装饰器模式、有什么应用场景、优缺点是什么?

装饰器模式定义

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构,装饰器模式的类图如下:

从IO中学习装饰器模式_第1张图片
装饰器类图

装饰器的角色介绍:

  • 装饰器抽象构件 => Component,为一个接口或抽象类
  • 具体的构建实现类 => ConcreteComponent
  • 装饰器类 => Decorator
  • 具体的装饰器类 => ConcreteDecoratorAConcreteDecoratorB,装饰器类的具体实现,里面必有一个属性指向Componet装饰器抽象构建

装饰器模式Demo

以写简历和hr读简历的例子来讲解装饰器模式

  • 抽象类或接口:Resume.java
    面向接口编程,所以选择了接口方式
    定义了一个自我介绍的方法
    public interface Resume {
        void selfIntroduce();
    }
    
  • 基本实现类:MyResume.java
    public class MyResume implements Resume {
        @Override
        public void selfIntroduce() {
            System.out.println("姓名: 何甜甜");
            System.out.println("求职方向:服务端开发");
        }
    }
    
    光有这些基本信息是不够的,需要有亮点,不然无法在寒冬求职中过简历关,所以我们得再装饰下简历,但前提要求是不动基本实现类
  • 装饰器类:Decorator.java
    public abstract class Decorator implements Resume {
        /**
         * 接口
         */
        private Resume resume;
    
        /**
         * 构造函数
         */
        public Decorator(Resume resume) {
            this.resume = resume;
        }
    
        @Override
        public void selfIntroduce() {
            //调用传入Resume实现类的方法
            resume.selfIntroduce();
        }
    }
    
  • 具体的装饰器类
    可以有多个具体的装饰器类
    • WorkExperienceDecorator.java
        public class WorkExperienceDecorator extends Decorator {
          /**
           * 构造函数
           *
           * @param resume
           */
          public WorkExperienceDecorator(Resume resume) {
              super(resume);
          }
      
          @Override
          public void selfIntroduce() {
             super.selfIntroduce();
             //在装饰一下简历,添加工作奖励
            addWorkExperience() 
          }
      
          public void addWorkExperience() {
              System.out.println("2018-2019:xxx公司打杂");
          }
        }
      
    • OtherDecorator.java
      public class OtherDecorator extends Decorator {
          /**
           * 构造函数
           *
           * @param resume
           */
          public OtherDecorator(Resume resume) {
              super(resume);
          }
      
          @Override
          public void selfIntroduce() {
              super.selfIntroduce();
              //添加其他信息,比如github、个人博客
              addOther();
          }
      
          public void addOther() {
              System.out.println("博客:https://juejin.im/user/5b8f8cc05188255c735f3f1d");
              System.out.println("github:https://github.com/TiantianUpup");
          }
      }
      
  • 测试类
    public class Test {
        public static void main(String[] args) {
            Resume resume = new MyResume();
            //1.添加工作经历
            resume = new WorkExperienceDecorator(resume);
            //2.添加github、博客
            resume = new OtherDecorator(resume);
    
            //hr读简历
            HrReader hrReader = new HrReader();
            hrReader.read(resume);
        }
    }
    

应用场景

装饰器模式就是使用在对已有的目标功能存在不足,需要增强时【不能改变已有类】,并且目标存在抽象接口
IO中应用了大量的装饰器模式,先来看一种类图:

从IO中学习装饰器模式_第2张图片
IO类图

附: 图片来源
Reader这块来讲解装饰器模式在IO中的应用

  • 抽象类
    Reader,是一个抽象类,里面定义了接口
  • 被装饰的类
    InputStreamReaderCharArrayReaderPipedReaderStringReaderFileReader
  • 装饰器类
    FilterReaderBufferedReader
  • 具体的装饰器类
    BufferedReaderPushbackReader
    FileReader尽管也在第三行,但是FileReader构不成一个具体的装饰器类,因为它不是BufferedReader的子类也不是FilterReader的子类,不持有Reader的引用
    读写IO的时候每次打开文件会非常耗时,我们可以使用BufferedReader装饰FileReader这些被装饰器类,使用缓冲提高性能

优缺点

  • 优点
    可以动态扩展一个实现类的功能而不改变原有实现类
  • 缺点
    由于使用装饰器模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是另一方面,由于使用装饰器模式会产生比使用继承关系更多的对象,更多的对象会使得查错变得困难,特别是这些对象看上去都很像

和继承有啥区别

装饰器模式是多继承的一种替代方式,但是两者之间还是存在一些不同。装饰器模式比较灵活,因为它修饰哪个类是在运行时才确定的,而继承中,继承哪个类是在编写哪个继承类的时候就要确定下来的,即继承是编译时确认

你可能感兴趣的:(从IO中学习装饰器模式)