装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern)

装饰器模式是一种结构型设计模式,它允许动态地给一个对象添加一些额外的行为,而无需修改其原始类。装饰器模式通过将对象包装在装饰器类中,来扩展对象的功能。这种方式比继承更加灵活,因为它可以在运行时动态地添加或移除功能。

模式结构

  1. 组件接口(Component)
    定义被装饰对象的接口,声明对象的方法。
  2. 具体组件(Concrete Component)
    实现组件接口,表示被装饰的原始对象。
  3. 装饰器抽象类(Decorator)
    实现组件接口,并持有一个组件对象的引用。它可以是抽象类,用于定义装饰器的基本结构。
  4. 具体装饰器(Concrete Decorator)
    继承装饰器抽象类,并在其中添加额外的行为。

代码示例

1. 组件接口(Component)

// 组件接口:定义被装饰对象的接口
interface DataSource {
    void write(String data);
    String read();
}

2. 具体组件(Concrete Component)

// 具体组件:文件源
class FileDataSource implements DataSource {
    private final String name;

    public FileDataSource(String name) {
        this.name = name;
    }

    @Override
    public void write(String data) {
        File file = new File(name);
        try (OutputStream out = new FileOutputStream(file)) {
            out.write(data.getBytes(), 0, data.length());
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    @Override
    public String read() {
        char[] buf = null;
        File file = new File(name);
        try (FileReader in = new FileReader(file)) {
            buf = new char[(int) file.length()];
            in.read(buf);
        } catch (IOException e) {
            System.out.println(e);
        }
        return new String(buf);
    }
}

3. 装饰器抽象类(Decorator)

// 装饰器抽象类:实现组件接口,并持有一个组件对象的引用
abstract class DataSourceDecorator implements DataSource {
    private final DataSource dataSource;

    public DataSourceDecorator(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void write(String data) {
        dataSource.write(data);
    }

    @Override
    public String read() {
        return dataSource.read();
    }
}

4. 具体装饰器(Concrete Decorator)

// 具体装饰器:加密装饰器
class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource dataSource) {
        super(dataSource);
    }

    @Override
    public void write(String data) {
        super.write(encode(data));
    }

    @Override
    public String read() {
        return decode(super.read());
    }

    // 加密
    private String encode(String data) {
        byte[] res = data.getBytes();
        for (int i = 0; i < res.length; i++) {
            res[i] += (byte) 1;
        }
        return Base64.getEncoder().encodeToString(res);
    }

    // 解密
    private String decode(String data) {
        byte[] res = Base64.getDecoder().decode(data);
        for (int i = 0; i < res.length; i++) {
            res[i] -= (byte) 1;
        }
        return new String(res);
    }
}

// 具体装饰器:压缩装饰器
class CompressionDecorator extends DataSourceDecorator {
    private int compLevel = 6;

    public CompressionDecorator(DataSource dataSource) {
        super(dataSource);
    }

    @Override
    public void write(String data) {
        super.write(compress(data));
    }

    @Override
    public String read() {
        return decompress(super.read());
    }

    // 压缩
    private String compress(String data) {
        byte[] src = data.getBytes();
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
            DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));
            dos.write(src);
            dos.close();
            bout.close();
            return Base64.getEncoder().encodeToString(bout.toByteArray());
        } catch (IOException ex) {
            return null;
        }
    }

    // 解压
    private String decompress(String data) {
        byte[] src = Base64.getDecoder().decode(data);
        try {
            InputStream in = new ByteArrayInputStream(src);
            InflaterInputStream iis = new InflaterInputStream(in);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(512);
            int len;
            while ((len = iis.read()) != -1) {
                bos.write(len);
            }
            bos.close();
            iis.close();
            in.close();
            return new String(bos.toByteArray());
        } catch (IOException ex) {
            return null;
        }
    }
}

5.调用示例

public class DecoratorPattern {
    public static void main(String[] args) {
        String data = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000";

        // 使用多层装饰器
        DataSourceDecorator encoded = new CompressionDecorator(new EncryptionDecorator(new FileDataSource("OutputDemo.txt")));
        encoded.write(data);

        // 读取数据
        DataSource dataSource = new FileDataSource("OutputDemo.txt");
        System.out.println("- INPUT --------------");
        System.out.println(data);
        System.out.println("- ENCODED ---------------");
        System.out.println(dataSource.read());
        System.out.println("- DECODED ---------------");
        System.out.println(encoded.read());
    }
}

输出结果

- INPUT --------------
Name,Salary
John Smith,100000
Steven Jobs,912000
- ENCODED ---------------
[加密后的数据]
- DECODED ---------------
Name,Salary
John Smith,100000
Steven Jobs,912000

应用场景

  1. 动态扩展功能
    当需要在不修改现有代码的情况下,动态地为对象添加功能时,可以使用装饰器模式。
  2. 替代继承
    当使用继承会导致类爆炸(类数量过多)时,可以使用装饰器模式来替代继承。
  3. 不可修改的类
    当需要扩展一个不可修改的类(如第三方库中的类)时,可以使用装饰器模式。

在Java中的应用

  • JAVA I/O

    image-20201020221653193

    1. InputStreamOutputStream 是组件接口。
    2. FileInputStreamFileOutputStream 是具体组件。
    3. BufferedInputStreamBufferedOutputStream 是具体装饰器,它们为输入输出流添加了缓冲功能。

优缺点

优点

  1. 灵活性
    可以在运行时动态地添加或移除功能,而不需要修改原始类。
  2. 避免类爆炸
    通过组合而不是继承来扩展功能,避免了类数量过多的问题。
  3. 单一职责原则
    每个装饰器类只负责一个特定的功能,符合单一职责原则。

缺点

  1. 复杂性增加
    使用装饰器模式会增加系统中的类数量,可能导致代码结构复杂。
  2. 调试困难
    由于装饰器是层层包装的,调试时可能需要逐层检查,增加了调试的难度。

总结

装饰器模式通过将对象包装在装饰器类中,提供了一种灵活的方式来扩展对象的功能。它适用于需要动态添加功能的场景,尤其是在不修改现有代码的情况下。尽管它可能会增加系统的复杂性,但其优点在于提高了代码的可维护性和灵活性。在Java中,装饰器模式广泛应用于I/O库中,是理解和使用该模式的经典示例。