深入理解设计模式之装饰者模式

深入理解设计模式之装饰者模式(Decorator Pattern)

一、引言

在软件开发中,我们经常需要给对象添加新的功能。最直接的方式是通过继承来扩展类,但这种方式会导致类的数量急剧增加,且不够灵活。装饰者模式提供了一种更加灵活的替代方案:在运行时动态地给对象添加职责,而不是通过继承。

本文将深入探讨装饰者模式的原理、实现方式,并结合Java I/O流、Spring框架等实际应用,帮助你全面掌握这一重要的设计模式。

二、什么是装饰者模式

2.1 定义

装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

2.2 核心思想

  • 动态扩展:在运行时给对象添加功能,而不是编译时
  • 透明性:装饰者和被装饰者实现相同的接口
  • 灵活组合:可以使用多个装饰者包装一个对象
  • 替代继承:使用组合而非继承来扩展功能

2.3 模式结构

scss 复制代码
┌──────────────────────┐
│    <<interface>>     │
│      Component       │    抽象组件
├──────────────────────┤
│ + operation()        │
└──────────────────────┘
           △
           │ 实现
     ┌─────┴──────┐
     │            │
┌────┴────────┐  ┌┴────────────────────┐
│  Concrete   │  │    Decorator        │   抽象装饰者
│  Component  │  ├─────────────────────┤
├─────────────┤  │ - component         │◆─→Component
│ operation() │  │ + operation()       │
└─────────────┘  └─────────△───────────┘
                           │ 继承
                    ┌──────┴──────┐
                    │             │
           ┌────────┴────┐  ┌────┴──────────┐
           │ConcreteA    │  │ConcreteB      │   具体装饰者
           │Decorator    │  │Decorator      │
           ├─────────────┤  ├───────────────┤
           │operation()  │  │operation()    │
           │addedBehavior│  │addedBehavior  │
           └─────────────┘  └───────────────┘

调用关系:
Client → ConcreteDecoratorA → ConcreteDecoratorB → ConcreteComponent
         (添加功能A)            (添加功能B)           (核心功能)

2.4 与继承的区别

arduino 复制代码
使用继承:
                  Component
                      │
        ┌─────────────┼─────────────┐
        │             │             │
   ComponentA    ComponentB    ComponentC
        │             │             │
   ┌────┴────┐   ┌───┴───┐    ┌───┴───┐
   │         │   │       │    │       │
  AA       AB   BA      BB   CA      CB   ← 类数量爆炸!

使用装饰者:
    Component
        │
    ┌───┴───┐
    │       │
Concrete  Decorator
            │
        ┌───┴───┐
    DecoratorA DecoratorB

组合方式:
new DecoratorA(new DecoratorB(new ConcreteComponent()))
灵活组合,避免类爆炸!

三、基础示例

3.1 场景:咖啡店饮料订单

咖啡店提供多种咖啡,顾客可以添加各种配料(牛奶、摩卡、糖浆等),每种配料都会增加价格。

抽象组件:

java 复制代码
/**
 * 抽象组件:饮料
 */
public interface Beverage {
    /**
     * 获取描述
     */
    String getDescription();

    /**
     * 获取价格
     */
    double cost();
}

具体组件:

java 复制代码
/**
 * 具体组件:浓缩咖啡
 */
public class Espresso implements Beverage {

    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }

    @Override
    public double cost() {
        return 15.0;
    }
}

/**
 * 具体组件:美式咖啡
 */
public class Americano implements Beverage {

    @Override
    public String getDescription() {
        return "美式咖啡";
    }

    @Override
    public double cost() {
        return 18.0;
    }
}

/**
 * 具体组件:拿铁
 */
public class Latte implements Beverage {

    @Override
    public String getDescription() {
        return "拿铁";
    }

    @Override
    public double cost() {
        return 22.0;
    }
}

抽象装饰者:

java 复制代码
/**
 * 抽象装饰者:调料装饰者
 */
public abstract class CondimentDecorator implements Beverage {

    /**
     * 被装饰的饮料对象
     */
    protected Beverage beverage;

    public CondimentDecorator(Beverage beverage) {
        this.beverage = beverage;
    }

    /**
     * 子类必须实现getDescription方法
     */
    @Override
    public abstract String getDescription();
}

具体装饰者:

java 复制代码
/**
 * 具体装饰者:牛奶
 */
public class Milk extends CondimentDecorator {

    public Milk(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 牛奶";
    }

    @Override
    public double cost() {
        return beverage.cost() + 3.0;
    }
}

/**
 * 具体装饰者:摩卡
 */
public class Mocha extends CondimentDecorator {

    public Mocha(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 摩卡";
    }

    @Override
    public double cost() {
        return beverage.cost() + 5.0;
    }
}

/**
 * 具体装饰者:豆浆
 */
public class Soy extends CondimentDecorator {

    public Soy(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 豆浆";
    }

    @Override
    public double cost() {
        return beverage.cost() + 4.0;
    }
}

/**
 * 具体装饰者:奶泡
 */
public class Whip extends CondimentDecorator {

    public Whip(Beverage beverage) {
        super(beverage);
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + 奶泡";
    }

    @Override
    public double cost() {
        return beverage.cost() + 2.0;
    }
}

客户端使用:

java 复制代码
public class CoffeeShop {
    public static void main(String[] args) {
        // 订单1: 纯浓缩咖啡
        Beverage beverage1 = new Espresso();
        System.out.println(beverage1.getDescription() + " ¥" + beverage1.cost());

        // 订单2: 美式咖啡 + 牛奶
        Beverage beverage2 = new Americano();
        beverage2 = new Milk(beverage2);
        System.out.println(beverage2.getDescription() + " ¥" + beverage2.cost());

        // 订单3: 拿铁 + 摩卡 + 奶泡(双重装饰)
        Beverage beverage3 = new Latte();
        beverage3 = new Mocha(beverage3);
        beverage3 = new Whip(beverage3);
        System.out.println(beverage3.getDescription() + " ¥" + beverage3.cost());

        // 订单4: 浓缩咖啡 + 双份摩卡 + 牛奶 + 奶泡(多重装饰)
        Beverage beverage4 = new Espresso();
        beverage4 = new Mocha(beverage4);
        beverage4 = new Mocha(beverage4);  // 双份摩卡
        beverage4 = new Milk(beverage4);
        beverage4 = new Whip(beverage4);
        System.out.println(beverage4.getDescription() + " ¥" + beverage4.cost());
    }
}

输出:

复制代码
浓缩咖啡 ¥15.0
美式咖啡 + 牛奶 ¥21.0
拿铁 + 摩卡 + 奶泡 ¥29.0
浓缩咖啡 + 摩卡 + 摩卡 + 牛奶 + 奶泡 ¥30.0

四、实际生产场景应用

4.1 场景:数据源装饰器

在企业应用中,我们需要对数据源添加各种功能:日志记录、性能监控、连接池管理等。

java 复制代码
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.io.PrintWriter;
import java.util.logging.Logger;

/**
 * 抽象组件:DataSource(JDK提供)
 * 我们使用javax.sql.DataSource接口
 */

/**
 * 具体组件:基础数据源
 */
public class BasicDataSource implements DataSource {

    private String url;
    private String username;
    private String password;

    public BasicDataSource(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    @Override
    public Connection getConnection() throws SQLException {
        System.out.println("创建数据库连接: " + url);
        // 实际实现中会使用DriverManager.getConnection()
        return null; // 简化示例
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return getConnection();
    }

    // 其他DataSource接口方法省略...
    @Override
    public PrintWriter getLogWriter() throws SQLException { return null; }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {}

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {}

    @Override
    public int getLoginTimeout() throws SQLException { return 0; }

    @Override
    public Logger getParentLogger() { return null; }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException { return null; }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
}

/**
 * 抽象装饰者:数据源装饰器
 */
public abstract class DataSourceDecorator implements DataSource {

    protected DataSource dataSource;

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

    @Override
    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return dataSource.getConnection(username, password);
    }

    // 委托其他方法
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return dataSource.getLogWriter();
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        dataSource.setLogWriter(out);
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        dataSource.setLoginTimeout(seconds);
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return dataSource.getLoginTimeout();
    }

    @Override
    public Logger getParentLogger() {
        return dataSource.getParentLogger();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return dataSource.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return dataSource.isWrapperFor(iface);
    }
}

具体装饰者:

java 复制代码
/**
 * 具体装饰者:日志记录数据源
 */
public class LoggingDataSource extends DataSourceDecorator {

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

    @Override
    public Connection getConnection() throws SQLException {
        long startTime = System.currentTimeMillis();

        System.out.println("[LOG] 开始获取数据库连接...");

        Connection connection = super.getConnection();

        long endTime = System.currentTimeMillis();
        System.out.println("[LOG] 获取连接成功,耗时: " + (endTime - startTime) + "ms");

        return connection;
    }
}

/**
 * 具体装饰者:性能监控数据源
 */
public class PerformanceMonitoringDataSource extends DataSourceDecorator {

    private static int connectionCount = 0;
    private static long totalConnectionTime = 0;

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

    @Override
    public Connection getConnection() throws SQLException {
        long startTime = System.nanoTime();

        Connection connection = super.getConnection();

        long endTime = System.nanoTime();
        long duration = (endTime - startTime) / 1_000_000; // 转换为毫秒

        connectionCount++;
        totalConnectionTime += duration;

        System.out.println("[PERFORMANCE] 连接次数: " + connectionCount +
                         ", 平均耗时: " + (totalConnectionTime / connectionCount) + "ms");

        return connection;
    }

    public static void printStatistics() {
        System.out.println("\n=== 性能统计 ===");
        System.out.println("总连接次数: " + connectionCount);
        System.out.println("总耗时: " + totalConnectionTime + "ms");
        System.out.println("平均耗时: " + (connectionCount > 0 ? totalConnectionTime / connectionCount : 0) + "ms");
    }
}

/**
 * 具体装饰者:重试数据源
 */
public class RetryDataSource extends DataSourceDecorator {

    private int maxRetries;

    public RetryDataSource(DataSource dataSource, int maxRetries) {
        super(dataSource);
        this.maxRetries = maxRetries;
    }

    @Override
    public Connection getConnection() throws SQLException {
        SQLException lastException = null;

        for (int i = 0; i <= maxRetries; i++) {
            try {
                if (i > 0) {
                    System.out.println("[RETRY] 第 " + i + " 次重试...");
                }
                return super.getConnection();
            } catch (SQLException e) {
                lastException = e;
                if (i < maxRetries) {
                    try {
                        Thread.sleep(1000 * (i + 1)); // 递增等待时间
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }

        throw new SQLException("获取连接失败,已重试 " + maxRetries + " 次", lastException);
    }
}

/**
 * 具体装饰者:连接池数据源(简化版)
 */
public class PooledDataSource extends DataSourceDecorator {

    private java.util.Queue<Connection> connectionPool = new java.util.LinkedList<>();
    private int poolSize;

    public PooledDataSource(DataSource dataSource, int poolSize) {
        super(dataSource);
        this.poolSize = poolSize;
        initializePool();
    }

    private void initializePool() {
        System.out.println("[POOL] 初始化连接池,大小: " + poolSize);
        for (int i = 0; i < poolSize; i++) {
            try {
                connectionPool.offer(super.getConnection());
            } catch (SQLException e) {
                System.err.println("初始化连接池失败: " + e.getMessage());
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (!connectionPool.isEmpty()) {
            System.out.println("[POOL] 从连接池获取连接,剩余: " + connectionPool.size());
            return connectionPool.poll();
        } else {
            System.out.println("[POOL] 连接池已空,创建新连接");
            return super.getConnection();
        }
    }

    public void returnConnection(Connection connection) {
        if (connectionPool.size() < poolSize) {
            connectionPool.offer(connection);
            System.out.println("[POOL] 连接返回连接池,当前: " + connectionPool.size());
        }
    }
}

客户端使用:

java 复制代码
public class DataSourceDecoratorDemo {
    public static void main(String[] args) throws SQLException {
        // 创建基础数据源
        DataSource basicDataSource = new BasicDataSource(
            "jdbc:mysql://localhost:3306/mydb",
            "root",
            "password"
        );

        // 场景1: 添加日志功能
        System.out.println("=== 场景1: 基础数据源 + 日志 ===");
        DataSource loggedDataSource = new LoggingDataSource(basicDataSource);
        loggedDataSource.getConnection();

        // 场景2: 添加日志 + 性能监控
        System.out.println("\n=== 场景2: 基础数据源 + 日志 + 性能监控 ===");
        DataSource monitoredDataSource = new PerformanceMonitoringDataSource(
            new LoggingDataSource(basicDataSource)
        );
        monitoredDataSource.getConnection();
        monitoredDataSource.getConnection();

        // 场景3: 添加日志 + 性能监控 + 重试
        System.out.println("\n=== 场景3: 完整装饰链 ===");
        DataSource fullDataSource = new RetryDataSource(
            new PerformanceMonitoringDataSource(
                new LoggingDataSource(basicDataSource)
            ),
            3  // 最多重试3次
        );
        fullDataSource.getConnection();

        // 打印统计信息
        PerformanceMonitoringDataSource.printStatistics();
    }
}

4.2 场景:HTTP请求/响应装饰器

在Web应用中,我们需要对HTTP请求和响应进行各种处理:压缩、加密、缓存等。

java 复制代码
/**
 * 抽象组件:HTTP处理器
 */
public interface HttpHandler {
    /**
     * 处理HTTP请求
     */
    String handleRequest(String request);
}

/**
 * 具体组件:基础HTTP处理器
 */
public class BasicHttpHandler implements HttpHandler {

    @Override
    public String handleRequest(String request) {
        // 模拟处理请求
        return "Response for: " + request;
    }
}

/**
 * 抽象装饰者:HTTP处理器装饰器
 */
public abstract class HttpHandlerDecorator implements HttpHandler {

    protected HttpHandler handler;

    public HttpHandlerDecorator(HttpHandler handler) {
        this.handler = handler;
    }

    @Override
    public String handleRequest(String request) {
        return handler.handleRequest(request);
    }
}

/**
 * 具体装饰者:压缩装饰器
 */
public class CompressionDecorator extends HttpHandlerDecorator {

    public CompressionDecorator(HttpHandler handler) {
        super(handler);
    }

    @Override
    public String handleRequest(String request) {
        // 解压请求
        String decompressedRequest = decompress(request);
        System.out.println("[COMPRESSION] 请求已解压");

        // 调用下一个处理器
        String response = super.handleRequest(decompressedRequest);

        // 压缩响应
        String compressedResponse = compress(response);
        System.out.println("[COMPRESSION] 响应已压缩");

        return compressedResponse;
    }

    private String decompress(String data) {
        return data; // 简化实现
    }

    private String compress(String data) {
        return "[COMPRESSED]" + data;
    }
}

/**
 * 具体装饰者:加密装饰器
 */
public class EncryptionDecorator extends HttpHandlerDecorator {

    public EncryptionDecorator(HttpHandler handler) {
        super(handler);
    }

    @Override
    public String handleRequest(String request) {
        // 解密请求
        String decryptedRequest = decrypt(request);
        System.out.println("[ENCRYPTION] 请求已解密");

        // 调用下一个处理器
        String response = super.handleRequest(decryptedRequest);

        // 加密响应
        String encryptedResponse = encrypt(response);
        System.out.println("[ENCRYPTION] 响应已加密");

        return encryptedResponse;
    }

    private String decrypt(String data) {
        return data.replace("[ENCRYPTED]", "");
    }

    private String encrypt(String data) {
        return "[ENCRYPTED]" + data;
    }
}

/**
 * 具体装饰者:缓存装饰器
 */
public class CacheDecorator extends HttpHandlerDecorator {

    private java.util.Map<String, String> cache = new java.util.HashMap<>();

    public CacheDecorator(HttpHandler handler) {
        super(handler);
    }

    @Override
    public String handleRequest(String request) {
        // 检查缓存
        if (cache.containsKey(request)) {
            System.out.println("[CACHE] 命中缓存");
            return cache.get(request);
        }

        System.out.println("[CACHE] 未命中缓存,执行请求");

        // 调用下一个处理器
        String response = super.handleRequest(request);

        // 存入缓存
        cache.put(request, response);
        System.out.println("[CACHE] 响应已缓存");

        return response;
    }
}

/**
 * 具体装饰者:日志装饰器
 */
public class LoggingDecorator extends HttpHandlerDecorator {

    public LoggingDecorator(HttpHandler handler) {
        super(handler);
    }

    @Override
    public String handleRequest(String request) {
        long startTime = System.currentTimeMillis();

        System.out.println("\n[LOG] ========== 请求开始 ==========");
        System.out.println("[LOG] 请求内容: " + request);
        System.out.println("[LOG] 时间: " + new java.util.Date());

        String response = super.handleRequest(request);

        long endTime = System.currentTimeMillis();

        System.out.println("[LOG] 响应内容: " + response);
        System.out.println("[LOG] 耗时: " + (endTime - startTime) + "ms");
        System.out.println("[LOG] ========== 请求结束 ==========\n");

        return response;
    }
}

/**
 * 客户端测试
 */
public class HttpHandlerDemo {
    public static void main(String[] args) {
        // 创建基础处理器
        HttpHandler basicHandler = new BasicHttpHandler();

        // 场景1: 基础处理器 + 日志
        System.out.println("=== 场景1: 基础 + 日志 ===");
        HttpHandler handler1 = new LoggingDecorator(basicHandler);
        handler1.handleRequest("GET /api/users");

        // 场景2: 基础 + 缓存 + 日志
        System.out.println("\n=== 场景2: 基础 + 缓存 + 日志 ===");
        HttpHandler handler2 = new LoggingDecorator(
            new CacheDecorator(basicHandler)
        );
        handler2.handleRequest("GET /api/products");
        handler2.handleRequest("GET /api/products"); // 第二次应该命中缓存

        // 场景3: 完整装饰链
        System.out.println("\n=== 场景3: 完整装饰链 ===");
        HttpHandler handler3 = new LoggingDecorator(
            new EncryptionDecorator(
                new CompressionDecorator(
                    new CacheDecorator(basicHandler)
                )
            )
        );
        String response = handler3.handleRequest("GET /api/sensitive-data");
        System.out.println("最终响应: " + response);
    }
}

五、开源框架中的应用

5.1 Java I/O流 - 装饰者模式的经典应用

Java的I/O流是装饰者模式最经典的应用案例。

java 复制代码
import java.io.*;

/**
 * Java I/O流的装饰者模式结构
 *
 * InputStream (抽象组件)
 *     ↑
 *     ├─ FileInputStream (具体组件)
 *     ├─ ByteArrayInputStream (具体组件)
 *     └─ FilterInputStream (抽象装饰者)
 *            ↑
 *            ├─ BufferedInputStream (具体装饰者)
 *            ├─ DataInputStream (具体装饰者)
 *            └─ PushbackInputStream (具体装饰者)
 */
public class JavaIODecoratorExample {

    public static void main(String[] args) throws IOException {
        // 示例1: 文件输入流 + 缓冲装饰器
        InputStream fileInput = new FileInputStream("data.txt");
        InputStream bufferedInput = new BufferedInputStream(fileInput);

        // 示例2: 文件输入流 + 缓冲 + 数据类型装饰器(多重装饰)
        DataInputStream dataInput = new DataInputStream(
            new BufferedInputStream(
                new FileInputStream("numbers.dat")
            )
        );

        // 读取不同类型的数据
        int number = dataInput.readInt();
        double decimal = dataInput.readDouble();
        String text = dataInput.readUTF();

        dataInput.close();

        // 示例3: 输出流的装饰
        OutputStream fileOutput = new FileOutputStream("output.txt");
        OutputStream bufferedOutput = new BufferedOutputStream(fileOutput);
        DataOutputStream dataOutput = new DataOutputStream(bufferedOutput);

        dataOutput.writeInt(42);
        dataOutput.writeDouble(3.14);
        dataOutput.writeUTF("Hello, Decorator!");

        dataOutput.close();
    }
}

/**
 * 自定义I/O装饰者:转换为大写
 */
class UpperCaseInputStream extends FilterInputStream {

    public UpperCaseInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();
        return (c == -1 ? c : Character.toUpperCase(c));
    }

    @Override
    public int read(byte[] b, int offset, int len) throws IOException {
        int result = super.read(b, offset, len);
        for (int i = offset; i < offset + result; i++) {
            b[i] = (byte) Character.toUpperCase((char) b[i]);
        }
        return result;
    }
}

/**
 * 自定义I/O装饰者:行号装饰器
 */
class LineNumberInputStream extends FilterInputStream {

    private int lineNumber = 0;
    private boolean newLine = true;

    public LineNumberInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read() throws IOException {
        int c = super.read();

        if (newLine && c != -1) {
            System.out.print(++lineNumber + ": ");
            newLine = false;
        }

        if (c == '\n') {
            newLine = true;
        }

        return c;
    }

    public int getLineNumber() {
        return lineNumber;
    }
}

/**
 * 使用自定义装饰者
 */
class CustomDecoratorDemo {
    public static void main(String[] args) throws IOException {
        // 测试大写转换装饰器
        InputStream input1 = new UpperCaseInputStream(
            new ByteArrayInputStream("Hello World".getBytes())
        );

        int c;
        while ((c = input1.read()) != -1) {
            System.out.print((char) c);
        }
        input1.close();

        System.out.println("\n");

        // 测试行号装饰器
        String text = "Line 1\nLine 2\nLine 3\n";
        InputStream input2 = new LineNumberInputStream(
            new ByteArrayInputStream(text.getBytes())
        );

        while ((c = input2.read()) != -1) {
            System.out.print((char) c);
        }
        input2.close();
    }
}

5.2 Spring框架中的应用

java 复制代码
/**
 * Spring中的BeanWrapper装饰者模式
 * BeanWrapper为Bean提供了额外的功能:属性访问、类型转换等
 */
class SpringBeanWrapperExample {

    public static void demonstrateBeanWrapper() {
        User user = new User();

        // BeanWrapper装饰了User对象,提供了额外功能
        org.springframework.beans.BeanWrapper wrapper =
            org.springframework.beans.PropertyAccessorFactory.forBeanPropertyAccess(user);

        // 通过BeanWrapper设置属性(支持类型转换)
        wrapper.setPropertyValue("name", "John");
        wrapper.setPropertyValue("age", "30"); // 字符串自动转换为int

        System.out.println(user.getName()); // John
        System.out.println(user.getAge());  // 30
    }
}

/**
 * Spring中的TransactionProxyFactoryBean
 * 为Bean添加事务管理功能
 */
class SpringTransactionDecoratorExample {

    /*
     * 配置示例(XML):
     *
     * <bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
     *     <property name="transactionManager" ref="transactionManager"/>
     *     <property name="target" ref="userServiceTarget"/>
     *     <property name="transactionAttributes">
     *         <props>
     *             <prop key="save*">PROPAGATION_REQUIRED</prop>
     *             <prop key="delete*">PROPAGATION_REQUIRED</prop>
     *         </props>
     *     </property>
     * </bean>
     *
     * 实际得到的userService是被事务装饰后的代理对象
     */
}

/**
 * Spring Cache装饰器
 */
class SpringCacheDecoratorExample {

    // 使用@Cacheable注解装饰方法,添加缓存功能
    // @Cacheable("users")
    public User getUserById(Long id) {
        System.out.println("从数据库查询用户: " + id);
        // 实际数据库查询
        return new User();
    }

    // Spring会创建一个代理对象,装饰getUserById方法
    // 添加缓存检查和缓存存储的功能
}

5.3 Servlet API中的应用

java 复制代码
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

/**
 * HttpServletRequestWrapper和HttpServletResponseWrapper
 * 是Servlet API提供的装饰者基类
 */
public class RequestResponseDecoratorExample {

    /**
     * 自定义请求装饰器:添加请求日志
     */
    static class LoggingRequestWrapper extends HttpServletRequestWrapper {

        public LoggingRequestWrapper(HttpServletRequest request) {
            super(request);
        }

        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            System.out.println("[REQUEST] 参数 " + name + " = " + value);
            return value;
        }
    }

    /**
     * 自定义响应装饰器:添加响应头
     */
    static class CustomHeaderResponseWrapper extends HttpServletResponseWrapper {

        public CustomHeaderResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public void setStatus(int sc) {
            super.setStatus(sc);
            super.setHeader("X-Custom-Header", "Decorated-Response");
            System.out.println("[RESPONSE] 添加自定义响应头");
        }
    }

    /**
     * 过滤器中使用装饰器
     */
    static class DecoratorFilter implements Filter {

        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                           FilterChain chain) throws IOException, ServletException {

            // 装饰请求和响应
            HttpServletRequest decoratedRequest =
                new LoggingRequestWrapper((HttpServletRequest) request);

            HttpServletResponse decoratedResponse =
                new CustomHeaderResponseWrapper((HttpServletResponse) response);

            // 传递装饰后的对象
            chain.doFilter(decoratedRequest, decoratedResponse);
        }

        @Override
        public void destroy() {
        }
    }
}

5.4 MyBatis中的应用

java 复制代码
/**
 * MyBatis的Executor装饰器
 * MyBatis使用装饰者模式为Executor添加缓存功能
 */
class MyBatisExecutorDecoratorExample {

    /*
     * MyBatis的Executor层次结构:
     *
     * Executor (接口)
     *     ↑
     *     ├─ BaseExecutor (抽象基类)
     *     │      ↑
     *     │      ├─ SimpleExecutor (具体实现)
     *     │      ├─ ReuseExecutor (具体实现)
     *     │      └─ BatchExecutor (具体实现)
     *     │
     *     └─ CachingExecutor (装饰器)
     *            装饰任何Executor,添加二级缓存功能
     *
     * 创建过程:
     * Executor executor = new SimpleExecutor();
     * executor = new CachingExecutor(executor); // 装饰,添加缓存
     */

    public void demonstrateMyBatisDecorator() {
        // 简化的MyBatis Executor装饰示例

        // 1. 创建基础执行器
        // Executor executor = new SimpleExecutor(configuration, transaction);

        // 2. 如果启用了二级缓存,使用CachingExecutor装饰
        // if (cacheEnabled) {
        //     executor = new CachingExecutor(executor);
        // }

        // 3. 应用插件(也是装饰器)
        // executor = (Executor) interceptorChain.pluginAll(executor);
    }
}

/**
 * 简化的MyBatis Executor装饰器实现
 */
interface Executor {
    <E> java.util.List<E> query(String sql, Object parameter);
}

class SimpleExecutor implements Executor {
    @Override
    public <E> java.util.List<E> query(String sql, Object parameter) {
        System.out.println("执行SQL: " + sql);
        return new java.util.ArrayList<>();
    }
}

class CachingExecutor implements Executor {

    private Executor delegate;
    private java.util.Map<String, java.util.List<?>> cache = new java.util.HashMap<>();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
    }

    @Override
    public <E> java.util.List<E> query(String sql, Object parameter) {
        String cacheKey = sql + ":" + parameter;

        // 检查缓存
        if (cache.containsKey(cacheKey)) {
            System.out.println("[CACHE] 命中缓存");
            return (java.util.List<E>) cache.get(cacheKey);
        }

        // 执行查询
        java.util.List<E> result = delegate.query(sql, parameter);

        // 存入缓存
        cache.put(cacheKey, result);
        System.out.println("[CACHE] 结果已缓存");

        return result;
    }
}

六、装饰者模式的优缺点

6.1 优点

1. 比继承更灵活

scala 复制代码
继承:编译时静态确定
class SpecialComponent extends Component {
    // 功能固定
}

装饰者:运行时动态组合
Component component = new ConcreteComponent();
component = new DecoratorA(component);
component = new DecoratorB(component);
// 功能可以灵活组合

2. 遵循开闭原则

  • 添加新装饰器无需修改现有代码
  • 可以随意组合装饰器

3. 遵循单一职责原则

  • 每个装饰器只负责一个功能
  • 可以根据需要选择装饰器

4. 可以动态添加/撤销功能

java 复制代码
// 运行时添加功能
Component component = new ConcreteComponent();
if (needLogging) {
    component = new LoggingDecorator(component);
}
if (needCaching) {
    component = new CachingDecorator(component);
}

6.2 缺点

1. 产生很多小对象

arduino 复制代码
装饰链:
new DecoratorD(
    new DecoratorC(
        new DecoratorB(
            new DecoratorA(
                new ConcreteComponent()
            )
        )
    )
)
创建了5个对象!

2. 调试困难

  • 多层装饰导致堆栈跟踪复杂
  • 难以理解最终对象的行为

3. 装饰顺序可能影响结果

java 复制代码
// 顺序1: 先加密后压缩
new CompressionDecorator(new EncryptionDecorator(component))

// 顺序2: 先压缩后加密
new EncryptionDecorator(new CompressionDecorator(component))

// 结果可能不同!

七、最佳实践

7.1 提供便捷的构建方法

java 复制代码
/**
 * 装饰器构建器
 */
public class DataSourceBuilder {

    private DataSource dataSource;

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

    public DataSourceBuilder withLogging() {
        this.dataSource = new LoggingDataSource(dataSource);
        return this;
    }

    public DataSourceBuilder withPerformanceMonitoring() {
        this.dataSource = new PerformanceMonitoringDataSource(dataSource);
        return this;
    }

    public DataSourceBuilder withRetry(int maxRetries) {
        this.dataSource = new RetryDataSource(dataSource, maxRetries);
        return this;
    }

    public DataSourceBuilder withPool(int poolSize) {
        this.dataSource = new PooledDataSource(dataSource, poolSize);
        return this;
    }

    public DataSource build() {
        return dataSource;
    }
}

/**
 * 使用构建器
 */
class BuilderDemo {
    public static void main(String[] args) {
        DataSource dataSource = new DataSourceBuilder(
                new BasicDataSource("jdbc:mysql://localhost/db", "user", "pass")
            )
            .withLogging()
            .withPerformanceMonitoring()
            .withRetry(3)
            .withPool(10)
            .build();

        // 清晰、易读的装饰器组合
    }
}

7.2 使用接口而非抽象类

java 复制代码
/**
 * 使用接口作为抽象装饰者(更灵活)
 */
public interface MessageService {
    void sendMessage(String message);
}

/**
 * 装饰器实现接口并持有接口引用
 */
public class EncryptedMessageService implements MessageService {

    private final MessageService messageService;

    public EncryptedMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    @Override
    public void sendMessage(String message) {
        String encrypted = encrypt(message);
        messageService.sendMessage(encrypted);
    }

    private String encrypt(String message) {
        return "[ENCRYPTED]" + message;
    }
}

7.3 考虑使用Java 8+的函数式接口

java 复制代码
import java.util.function.Function;

/**
 * 使用函数式编程实现装饰器
 */
public class FunctionalDecorator {

    public static void main(String[] args) {
        // 基础函数
        Function<String, String> base = input -> "Processed: " + input;

        // 装饰器:添加日志
        Function<String, String> withLogging = input -> {
            System.out.println("Input: " + input);
            String result = base.apply(input);
            System.out.println("Output: " + result);
            return result;
        };

        // 装饰器:转换为大写
        Function<String, String> withUpperCase = input ->
            base.apply(input).toUpperCase();

        // 组合装饰器
        Function<String, String> combined = base
            .andThen(String::toUpperCase)
            .andThen(s -> "[DECORATED]" + s);

        System.out.println(combined.apply("hello"));
        // 输出: [DECORATED]PROCESSED: HELLO
    }
}

7.4 文档化装饰器的影响

java 复制代码
/**
 * 数据源装饰器基类
 *
 * 使用指南:
 * 1. 装饰器可以任意组合
 * 2. 建议的装饰顺序:日志 → 监控 → 重试 → 连接池
 * 3. 性能影响:每个装饰器增加约0.1ms开销
 *
 * 示例:
 * <pre>
 * DataSource ds = new BasicDataSource(...);
 * ds = new LoggingDataSource(ds);              // +0.1ms
 * ds = new PerformanceMonitoringDataSource(ds); // +0.1ms
 * ds = new RetryDataSource(ds, 3);             // +0-3000ms (失败时)
 * ds = new PooledDataSource(ds, 10);           // -10ms (池化后)
 * </pre>
 *
 * 注意事项:
 * - 避免过度装饰(建议不超过4层)
 * - 装饰顺序会影响行为
 * - 某些装饰器可能不兼容
 */
public abstract class DocumentedDataSourceDecorator extends DataSourceDecorator {
    public DocumentedDataSourceDecorator(DataSource dataSource) {
        super(dataSource);
    }
}

八、总结

8.1 核心要点

  1. 装饰者模式的本质:动态地给对象添加职责,而不改变其结构
  2. 适用场景
    • 需要动态添加功能
    • 使用继承会导致类爆炸
    • 需要灵活组合多种功能
  3. 关键原则
    • 装饰者和被装饰者有相同的接口
    • 装饰者持有被装饰者的引用
    • 可以透明地替换被装饰者

8.2 使用建议

arduino 复制代码
选择装饰者模式的检查清单:

✓ 是否需要在运行时动态添加功能?
✓ 是否有多种功能可以任意组合?
✓ 使用继承是否会导致类数量爆炸?
✓ 是否需要对某些对象添加功能,而其他对象不需要?

如果以上都是"是",那么装饰者模式是个好选择!

8.3 与其他模式的对比

diff 复制代码
装饰者 vs 代理模式:
- 装饰者:增强功能
- 代理:控制访问

装饰者 vs 适配器模式:
- 装饰者:保持接口不变,增加功能
- 适配器:改变接口,使其兼容

装饰者 vs 组合模式:
- 装饰者:只有一个子组件
- 组合:可以有多个子组件

8.4 实践经验

  1. 保持简单:不要过度装饰(建议不超过3-4层)
  2. 注意顺序:某些装饰器的顺序会影响结果
  3. 提供便捷方法:使用Builder或工厂简化装饰器创建
  4. 文档化:清楚说明各装饰器的作用和影响

相关推荐
雨中飘荡的记忆1 小时前
秒杀系统设计与实现
java·redis·lua
小坏讲微服务2 小时前
Spring Cloud Alibaba 整合 Scala 教程完整使用
java·开发语言·分布式·spring cloud·sentinel·scala·后端开发
老鼠只爱大米2 小时前
Java设计模式之外观模式(Facade)详解
java·设计模式·外观模式·facade·java设计模式
qq_172805592 小时前
Go 语言结构型设计模式深度解析
开发语言·设计模式·golang
vx_dmxq2112 小时前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
9号达人2 小时前
优惠系统演进:从"实时结算"到"所见即所得",前端传参真的鸡肋吗?
java·后端·面试
AAA简单玩转程序设计2 小时前
Java进阶小妙招:ArrayList和LinkedList的"相爱相杀"
java
lkbhua莱克瓦242 小时前
集合进阶8——Stream流
java·开发语言·笔记·github·stream流·学习方法·集合
20岁30年经验的码农3 小时前
Java Elasticsearch 实战指南
java·开发语言·elasticsearch