设计模式(十)结构型:装饰器模式详解

装饰器模式(Decorator Pattern)是 GoF 23 种设计模式中的结构型模式之一,其核心价值在于动态地为对象添加额外的职责或功能,而无需通过继承来扩展类。它提供了一种比继承更灵活、更可组合的扩展机制,能够在运行时根据需要“包装”对象,实现功能的叠加与复用。装饰器模式是实现“开闭原则”的典范,广泛应用于 I/O 流处理、Web 过滤器链、缓存增强、日志记录、权限控制等需要动态增强对象行为的场景,是构建可扩展、可配置系统的关键工具。

一、详细介绍

装饰器模式解决的是“静态继承导致类爆炸和灵活性不足”的问题。在传统面向对象设计中,若要为一个类添加新功能,通常通过继承创建子类(如 BufferedFileReaderLoggedFileReader)。但当多个功能需要组合时(如带缓冲和日志的读取器),子类数量呈指数增长,且无法在运行时动态调整功能。

装饰器模式通过“组合 + 委托”的方式,将功能扩展从“类的层次结构”转移到“对象的运行时结构”。其核心思想是:保持接口不变,通过包装(wrapping)来增强行为

该模式包含以下核心角色:

  • Component(组件接口):定义被装饰对象的公共接口,客户端通过该接口与所有对象(原始对象或装饰后对象)交互。
  • ConcreteComponent(具体组件):实现 Component 接口,表示被装饰的基本对象,提供核心功能。
  • Decorator(装饰器抽象类):实现 Component 接口,并持有一个 Component 类型的引用(即被装饰的对象)。它通常是一个抽象类,封装了对被装饰对象的委托调用。
  • ConcreteDecorator(具体装饰器):继承 Decorator,在调用被装饰对象方法前后添加额外职责(如日志、缓存、压缩等)。可以有多个具体装饰器,它们可以按任意顺序组合。

装饰器模式的关键机制是递归包装:一个装饰器可以包装原始对象,也可以包装另一个装饰器,从而形成一条“装饰链”。客户端调用时,请求沿着链逐层传递,每层执行自己的逻辑后再委托给下一层,最终到达原始对象。

与“适配器模式”相比,装饰器不改变接口,而是增强行为;与“代理模式”相比,装饰器关注功能增强,代理关注访问控制(如延迟初始化、权限检查);与“桥接模式”相比,装饰器是运行时动态增强,桥接是结构分离。

装饰器模式的优势:

  • 动态扩展:功能可在运行时按需添加或移除。
  • 避免类爆炸:N 个功能只需 N 个装饰器类,而非 2^N 个组合子类。
  • 符合单一职责原则:每个装饰器只负责一个功能。
  • 透明性:客户端无需知道对象是否被装饰,接口保持一致。

二、装饰器模式的UML表示

以下是装饰器模式的标准 UML 类图:

implements
implements
extends
extends
has a
«interface»
Component
+operation()
ConcreteComponent
+operation()
Decorator
-component: Component
+Decorator(component: Component)
+operation()
ConcreteDecoratorA
+operation()
+addedBehaviorA()
ConcreteDecoratorB
+operation()
+addedBehaviorB()

图解说明

  • Component 是统一接口,所有对象(原始或装饰)都实现它。
  • ConcreteComponent 是被装饰的基本对象。
  • Decorator 是抽象装饰器,持有 Component 引用,实现 operation() 并委托给被装饰对象。
  • ConcreteDecoratorAConcreteDecoratorB 是具体装饰器,在 operation() 中添加额外行为。
  • 客户端通过组合多个装饰器,形成功能叠加链。

三、一个简单的Java程序实例及其UML图

以下是一个文本处理系统的示例,展示如何使用装饰器模式动态添加“大写转换”和“星号加密”功能。

Java 程序实例
// 组件接口:文本处理器
interface TextProcessor {
    String process(String text);
}

// 具体组件:基础文本处理器
class SimpleTextProcessor implements TextProcessor {
    @Override
    public String process(String text) {
        System.out.println("SimpleTextProcessor: Processing raw text");
        return text;
    }
}

// 抽象装饰器
abstract class TextProcessorDecorator implements TextProcessor {
    protected TextProcessor processor;

    public TextProcessorDecorator(TextProcessor processor) {
        this.processor = processor;
    }

    @Override
    public String process(String text) {
        return processor.process(text); // 默认委托
    }
}

// 具体装饰器:大写转换
class UppercaseDecorator extends TextProcessorDecorator {
    public UppercaseDecorator(TextProcessor processor) {
        super(processor);
    }

    @Override
    public String process(String text) {
        System.out.println("UppercaseDecorator: Converting to uppercase");
        String result = super.process(text);
        return result.toUpperCase();
    }
}

// 具体装饰器:星号加密(隐藏元音字母)
class StarEncryptionDecorator extends TextProcessorDecorator {
    public StarEncryptionDecorator(TextProcessor processor) {
        super(processor);
    }

    @Override
    public String process(String text) {
        System.out.println("StarEncryptionDecorator: Encrypting vowels with *");
        String result = super.process(text);
        return result.replaceAll("[aeiouAEIOU]", "*");
    }
}

// 客户端使用示例
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        // 创建基础处理器
        TextProcessor baseProcessor = new SimpleTextProcessor();

        // 动态装饰:仅大写
        TextProcessor uppercaseProcessor = new UppercaseDecorator(baseProcessor);

        // 动态装饰:大写 + 加密
        TextProcessor encryptedProcessor = new StarEncryptionDecorator(uppercaseProcessor);

        // 客户端统一调用,无需知道装饰结构
        System.out.println("=== Processing 'Hello World' ===");
        String result1 = baseProcessor.process("Hello World");
        System.out.println("Result: " + result1);

        System.out.println("\n=== Processing with Uppercase ===");
        String result2 = uppercaseProcessor.process("Hello World");
        System.out.println("Result: " + result2);

        System.out.println("\n=== Processing with Uppercase + Encryption ===");
        String result3 = encryptedProcessor.process("Hello World");
        System.out.println("Result: " + result3);

        // 演示装饰顺序的影响
        System.out.println("\n=== Reverse Order: Encrypt then Uppercase ===");
        TextProcessor reverseProcessor = new UppercaseDecorator(
            new StarEncryptionDecorator(baseProcessor)
        );
        String result4 = reverseProcessor.process("Hello World");
        System.out.println("Result: " + result4);
    }
}
实例对应的UML图(简化版)
has a
«interface»
TextProcessor
+process(text: String)
SimpleTextProcessor
+process(text: String)
TextProcessorDecorator
-processor: TextProcessor
+TextProcessorDecorator(processor: TextProcessor)
+process(text: String)
UppercaseDecorator
+process(text: String)
StarEncryptionDecorator
+process(text: String)

运行说明

  • TextProcessor 是统一接口。
  • SimpleTextProcessor 是基础实现。
  • TextProcessorDecorator 是抽象装饰器,持有被装饰对象。
  • UppercaseDecoratorStarEncryptionDecorator 添加具体功能。
  • 客户端通过组合装饰器实现功能叠加,且装饰顺序影响最终结果(如先加密后大写 vs 先大写后加密)。

四、总结

特性 说明
核心目的 动态添加职责,避免继承导致的类膨胀
实现机制 接口继承 + 对象组合 + 委托调用
优点 动态扩展、功能可组合、符合开闭原则
缺点 装饰链过长影响性能、调试复杂、可能产生大量小对象
适用场景 I/O 流、过滤器链、缓存、日志、权限、UI 效果增强
不适用场景 功能固定、性能敏感、接口频繁变更

装饰器模式使用建议

  • 保持装饰器轻量,避免在 process() 中执行耗时操作。
  • 注意装饰顺序,必要时提供构建器(Builder)或配置方式控制顺序。
  • 在 Java I/O 中,InputStream/OutputStream 体系是经典应用(如 BufferedInputStream(new FileInputStream(...)))。
  • 可结合工厂模式或依赖注入管理装饰链的创建。

架构师洞见:
装饰器模式是“关注点分离”与“运行时可配置性”的完美体现。在现代架构中,其思想已深入到中间件设计微服务治理函数式编程中。例如,在 Spring 框架中,AOP 的环绕通知本质上是装饰器;在 API 网关中,认证、限流、日志等插件通过装饰链处理请求;在 React 中,高阶组件(HOC)是装饰器的函数式变体。

未来趋势是:装饰器模式将与响应式编程结合,在数据流管道中动态插入处理节点;在Serverless 架构中,函数可通过装饰器链实现跨切面关注点(如监控、重试);在AI 应用中,提示词(Prompt)可被多个装饰器(如格式化、安全过滤、上下文增强)动态处理。

掌握装饰器模式,有助于设计出灵活、可插拔、易维护的系统。作为架构师,应倡导在系统边界(如服务入口、数据通道)使用装饰器分离核心逻辑与横切关注点。装饰器不仅是模式,更是运行时增强的哲学——它告诉我们:真正的灵活性,来自于对“包装”与“委托”的深刻理解。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐