1. 装饰器模式

1.1 介绍

一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,**并且随着扩展功能的增多,子类会很膨胀。**如何在不增加很多子类的情况下拓展现有类呢?

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

装饰器模式以对客户透明的方式动态地给一个对象附加上更多的责任。换而言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰器模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

实现步骤

  • 将原始类进行抽象
  • 生成抽象修饰类继承抽象原始类
  • 实现抽象修饰类,并通过注入实现原始类的拓展

1.2 优点

优点

  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点

  • 多层装饰比较复杂。

1.3 使用场景

  • 扩展一个类的功能

  • 动态增加功能,动态撤销。

1.4 注意事项

  • 可替代继承

2. 案例实现

现在,我要给车内加点内饰,我从商店里买一个布娃娃准备挂到车里,但是布娃娃的衣服不怎么好看,因此我给布娃娃上面套了一件裙子,这样它就更好看了。

// 布娃娃类
public class Ragdoll {
   
    public void work(){
   
        System.out.println("飘呀飘~");
    }
}

public class DecoratorRagdoll{
   
    private Ragdoll ragdoll;

    public DecoratorRagdoll(Ragdoll ragdoll) {
   
        this.ragdoll = ragdoll;
    }
    
    public void work(){
   
        this.wearSkirt();
        ragdoll.work();
    }

    private void wearSkirt() {
   
        System.out.println("布娃娃穿上了小裙子");
    }
}

public class Car {
   
    // 汽车无法使用普通的CDPlay
    private DecoratorRagdoll ragdoll;

    public Car(DecoratorRagdoll ragdoll) {
   
        this.ragdoll = ragdoll;
    }
    
    public void workRagdoll(){
   
        this.ragdoll.work();
    }
}

/** * 装饰器模式 * *@Author cly *@Date 2021/08/31 14:12 *@Version 1.0 */
public class Decorator {
   
    public static void main(String[] args) {
   
        DecoratorRagdoll decoratorRagdoll = new DecoratorRagdoll(new Ragdoll());
        Car car = new Car(decoratorRagdoll);
        car.workRagdoll();
    }
}

3. 源码实现

3.1 java.io.*

装饰器模式比较经典的应用就是 JDK 中的 java.io 包下,InputStream、OuputStream、Reader、Writer 及它们的子类。

以 InputStream 为例

  • FileInputStream 是 InputStream 的子类,用来读取文件字节流
  • BufferedInputStream 是 InputStream 的子类的子类,可缓存的字节流
  • DataInputStream 也是 InputStream 的子类的子类,可直接读取 Java 基本类型的字节流

如果希望提供一个可以读取文件 + 可缓存的字节流,使用继承方式,就需要派生 FileBufferedInputStream;

如果希望提供一个可以读取文件 + 直接读取基本类型的字节流,使用继承方式,就需要派生 FileDataInputStream。

字节流功能的增强还包括支持管道 pipe、字节数组 bytearray、字节对象 object、字节流字符流的转换 等维度,如果用继承方式,那类的层级与种类会多到爆炸。

为了解决问题,这边就使用了装饰器模式。