结构型-装饰器模式

154 阅读4分钟

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象自身结构的情况下,动态地添加新功能。它通过将对象放入特殊的包装类中(称为“装饰器”)来实现功能的扩展,是继承的一种灵活替代方案。


一、 核心思想

  1. 动态扩展:在运行时添加/移除功能,而非编译时
  2. 组合优于继承:通过对象组合实现功能叠加,避免类爆炸
  3. 透明性:装饰后的对象与原对象接口一致,对客户端透明

二、典型使用场景

  1. I/O 流操作(Java 原生) 如 InputStream, BufferedInputStream, DataInputStream 等,都是典型的装饰器模式应用
  2. 权限控制/日志记录等通用功能增强 在不改变核心业务逻辑的前提下,为服务添加日志、缓存、安全等功能。
  3. UI 组件扩展 比如按钮组件加上边框、阴影、滚动条等效果。
  4. Spring 中的 AOP(面向切面编程) Spring 使用了类似装饰器的思想来实现方法拦截和增强。

三、代码实现

1. 传统装饰器模式实现

以下是一个典型的装饰器模式实现,模拟为文本消息添加加密、压缩等动态功能。

1.1 定义接口(Component)
// 定义基础功能接口
public interface TextMessage {
    String getContent(); // 获取文本内容
}
1.2 实现具体组件(Concrete Component)
// 基础文本消息实现
public class PlainTextMessage implements TextMessage {
    private String content;

    public PlainTextMessage(String content) {
        this.content = content;
    }

    @Override
    public String getContent() {
        return content;
    }
}
1.3 定义装饰器抽象类(Decorator)
// 所有装饰器的基类,保持接口一致
public abstract class TextMessageDecorator implements TextMessage {
    protected TextMessage decoratedText;

    public TextMessageDecorator(TextMessage decoratedText) {
        this.decoratedText = decoratedText;
    }

    @Override
    public String getContent() {
        return decoratedText.getContent();
    }
}
1.4 实现具体装饰器(Concrete Decorators)
// 添加加密功能的装饰器
public class EncryptedMessageDecorator extends TextMessageDecorator {
    public EncryptedMessageDecorator(TextMessage decoratedText) {
        super(decoratedText);
    }

    @Override
    public String getContent() {
        String originalContent = super.getContent();
        // 简单加密(实际可用 AES 等算法)
        return encrypt(originalContent);
    }

    private String encrypt(String content) {
        // 模拟加密:将字符串反转
        return new StringBuilder(content).reverse().toString();
    }
}

// 添加压缩功能的装饰器
public class CompressedMessageDecorator extends TextMessageDecorator {
    public CompressedMessageDecorator(TextMessage decoratedText) {
        super(decoratedText);
    }

    @Override
    public String getContent() {
        String originalContent = super.getContent();
        // 简单压缩(实际可用 GZIP 等)
        return compress(originalContent);
    }

    private String compress(String content) {
        // 模拟压缩:移除空格
        return content.replaceAll(" ", "");
    }
}
1.5 客户端调用
public class Client {
    public static void main(String[] args) {
        // 原始文本
        TextMessage message = new PlainTextMessage("Hello, this is a secret message!");

        // 加密装饰
        TextMessage encryptedMessage = new EncryptedMessageDecorator(message);
        System.out.println("Encrypted: " + encryptedMessage.getContent());
        // 输出: "!egassem terces a si siht ,olleH"

        // 先加密再压缩
        TextMessage encryptedAndCompressedMessage = new CompressedMessageDecorator(
            new EncryptedMessageDecorator(message)
        );
        System.out.println("Encrypted & Compressed: " + encryptedAndCompressedMessage.getContent());
        // 输出: "!egassemtercesasith,olleH"
    }
}

2. Spring 中的装饰器思想实现

Spring 并没有直接使用传统的装饰器模式,但通过 AOP代理模式 实现了类似的功能动态增强。

2.1 使用 AOP 实现日志装饰
@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example.service.MessageService.processMessage(..))")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 执行原始方法
        long executionTime = System.currentTimeMillis() - start;
        System.out.println("方法耗时: " + executionTime + "ms");
        return result;
    }
}
2.2 使用 BeanPostProcessor 动态装饰 Bean
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MessageService) {
            // 动态包装目标 Bean
            return Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                new Class[]{MessageService.class},
                (proxy, method, args) -> {
                    System.out.println("装饰器模式拦截方法: " + method.getName());
                    return method.invoke(bean, args); // 调用原始方法
                });
        }
        return bean;
    }
}
2.3 Spring Security 的装饰器思想

Spring Security 的 FilterChain 类似责任链 + 装饰器模式:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new CustomAuthFilter(), UsernamePasswordAuthenticationFilter.class); // 类似装饰器添加新功能
        return http.build();
    }
}

3. 两种实现对比

方式优点缺点
传统装饰器模式显式控制装饰逻辑,适合业务层扩展需要手动编写装饰器类
Spring AOP与框架集成,适合非业务逻辑(日志、事务)仅适用于 Spring 管理的 Bean

4. 总结

  • 传统装饰器模式:适用于业务逻辑的动态增强(如消息处理、UI 组件扩展)。
  • Spring AOP:更适合系统级功能(日志、安全、缓存),通过注解和配置实现无侵入性增强。

如果需要更具体的场景(如在 Spring Boot 中实现某个业务功能的装饰),可以提供需求细节,我会进一步补充代码示例。

四、装饰器模式 vs 代理模式 vs 适配器模式

  • 装饰器模式:当你需要在运行时动态地为对象添加功能,并希望保持代码灵活性和可扩展性。
  • 代理模式:当你需要控制对某个对象的访问,比如进行权限检查、日志记录、缓存等。
  • 适配器模式:当你需要整合两个不兼容接口的类,使其能一起工作,而不想修改现有代码。

五、总结

  • 装饰器模式适用于需要在运行时动态增强对象行为的场景。
  • Spring 中虽未直接使用装饰器,但其 AOP、BeanPostProcessor、Security 过滤链等机制都体现了其思想。
  • 它是构建灵活、可扩展系统的重要手段之一。
  • 如果你有具体的业务需求,比如“如何在 Spring 中为某个服务动态添加日志、缓存等功能”,我可以为你展示具体的代码示例。

结合项目理解