Decorator装饰器模式个人理解

对于装饰器模式,其主要是为了:在不改变本体特征的情况下,对其进行包装、装饰,目的是为了补充、扩展、增强其功能。
有三个原则:

  1. 不能改变本体的特征
  2. 要对本体的功能进行扩展
  3. 装饰器脱离了本体则没有任何含义

下面是一个具体的例子:

对于一个水果类

public class Fruit {
      //代表水果的名字:苹果,西瓜,桃子,橘子等等。。。
    public String name;
    
    public Fruit(String name){
        this.name = name;
    }
    
    public String getName(){
        return this.name;
    }
    
    public void eat(){
        System.out.println(getName() + "被吃了");
    }
}

每一个水果都有一个“被吃”的方法

public static void main(String[] args) {
        
        Fruit apple = new Fruit("苹果");
        Fruit watermelon = new Fruit("西瓜");
        Fruit orange = new Fruit("橘子");
        Fruit peach = new Fruit("桃子");
        
        apple.eat();
        watermelon.eat();
        orange.eat();
        peach.eat();
        
    }

输出

苹果被吃了
西瓜被吃了
橘子被吃了
桃子被吃了


但现在我想对水果类添加一个功能--“被洗”

于是可以想到在Fruit类中添加一个功能wash()即可,并且在eat()方法中调用即可;

    public void wash(){
        System.out.println(getName() + "被洗了");
    }
    public void eat(){
        wash();
        System.out.println(getName() + "被吃了");
    }

但是实际上对于Fruit的一些其他水果是并不需要wash()这个方法的,例如橘子,西瓜等,他们是不需要执行wash()这个操作;所以这样子直接加在Fruit类上显然是不合适的

那么可能就会想到继承,只需要对要洗的水果创建一个类,并继承Fruit类,重写eat()方法即可;

例如:

public class Apple extends Fruit {
    
    public Apple(String name){
        super(name);
    }
    
    public void wash(){
        System.out.println(getName()+"被洗了");
    }

    @Override
    public void eat() {
        wash();
        super.eat();
    }
}

测试:

public static void main(String[] args) {
        
        //注意此处new 的是Apple对象,利用多态性质
        Fruit apple = new Apple("苹果");
        Fruit watermelon = new Fruit("西瓜");
        Fruit orange = new Fruit("橘子");
        Fruit peach = new Fruit("桃子");
        
        //调用Apple类的eat方法
        apple.eat();
        watermelon.eat();
        orange.eat();
        peach.eat();
    }

苹果被洗了
苹果被吃了
西瓜被吃了
橘子被吃了
桃子被吃了

我们可以看到苹果是被洗了的,但是对于桃子来说,桃子也需要被洗,那么就又需要写一个Peach类,那还有诸多需要洗的水果,也不能每一个都创建一个类然后重写eat()方法

于是我们就可以使用到装饰器模式,我们可以对需要洗的水果进行包装

装饰器(洗)中保存一个被装饰的对象(水果),之后对需要装饰的方法(吃)进行重写,并且进行装饰(被洗),之后再通过被装饰的对象(水果),去调用它本身的方法(吃)

public class WashDecorator {
    
    Fruit fruit;
    
    public WashDecorator(Fruit fruit){
        this.fruit = fruit;
    }
    
    public void eat(){
        //
        if(fruit != null){
            System.out.println(fruit.getName() + "被洗干净了");
            fruit.eat();
        }
    }
}

这样子我们就可以对需要洗的水果进行包装

public static void main(String[] args) {
        
        
        Fruit apple = new Fruit("苹果");
        //使用装饰器对苹果进行包装
        WashDecorator appleWash = new WashDecorator(apple);

        Fruit watermelon = new Fruit("西瓜");
        Fruit orange = new Fruit("橘子");

        Fruit peach = new Fruit("桃子");
        //使用装饰器对桃子进行包装
        WashDecorator peachWash = new WashDecorator(peach);

        //使用装饰器调用eat方法
        appleWash.eat();
        watermelon.eat();
        orange.eat();
        peachWash.eat();
    }

结果

苹果被洗干净了
苹果被吃了
西瓜被吃了
橘子被吃了
桃子被洗干净了
桃子被吃了

当然我们上面的案例可能不是很实际,下面是一个实际的例子,也是很经典的装饰器模式的案例

  • 也就是我们的FileInputStream与BufferedInputStream

  • 实际上BufferedInputStream就相当于FileInputStream的一种装饰器

我们在使用时常常使用BufferedInputStream去包装FileInputStream,之后去调用bis的read方法

        FileInputStream fis = new FileInputStream(path);
        BufferedInputStream bis = new BufferedInputStream(fis);

对于FileInputStream 来说,我们要添加一个缓存区功能,我们能在FileInputStream 本类来加吗?
不能,因为不是所有的FileInputStream 都需要一个缓存区,那么就使用到了装饰器模式,在不能改变本体的特征的情况下要对本体的功能进行扩展
使用一个BufferedInputStream 类来装饰FileInputStream 类,BufferedInputStream 里面实现了缓存的功能,当我们的业务逻辑需要用到缓存功能的时候,我们就可以对其进行包装

关于FileInputStream 与BufferedInputStream 详情见下一篇文章:IO流-字节流BufferedInputStream 底层实现

posted @ 2020-08-23 22:49  微花  阅读(140)  评论(0编辑  收藏  举报

Loading