Skip to content

装饰器模式

引言

在软件设计中,常需为对象动态添加功能(如日志记录、加密)。传统继承会导致类爆炸,且破坏开闭原则。装饰器模式通过嵌套包装对象的方式,在不修改原有结构的前提下,实现功能的灵活扩展,成为增强对象行为的首选方案。

诞生背景

GoF在《设计模式》中提出装饰器模式,解决以下痛点:

  1. 继承僵化:子类组合爆炸(如File+Encrypt+Compress需多重继承)
  2. 功能耦合:核心逻辑与附加功能混杂(如支付接口包含日志/验证代码)
  3. 动态扩展难:运行时无法自由增减功能

演进过程

  • GoF基础(1994):确立装饰器核心角色(组件、具体组件、装饰器基类)
  • 语言特性融合:Java的IO流BufferedInputStream包装FileInputStream),PHP的PSR-7(中间件装饰HTTP请求)
  • 现代演进:支持函数式装饰(Python装饰器语法)、异步装饰(Node.js中间件)

核心概念

  • 组件接口(Component)
    • 定义核心功能(如read()
  • 具体组件(ConcreteComponent)
    • 实现基础功能(如文本文件读取)
  • 装饰器基类(Decorator)
    • 持有组件引用,实现相同接口
  • 具体装饰器(ConcreteDecorator)
    • 添加扩展功能(如加密/压缩)

通用实现

Java 实现

java
// 组件接口
interface DataSource {
    void write(String data);
    String read();
}

// 具体组件
class FileDataSource implements DataSource {
    public void write(String data) { /* 写入文件 */ }
    public String read() { /* 读取文件 */ return ""; }
}

// 装饰器基类
abstract class DataSourceDecorator implements DataSource {
    protected DataSource wrappee;
    DataSourceDecorator(DataSource source) { this.wrappee = source; }
}

// 加密装饰器
class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource source) { super(source); }
    public void write(String data) {
        wrappee.write(encrypt(data)); // 扩展功能
    }
    private String encrypt(String data) { return "ENCRYPTED:" + data; }
}

// 客户端
DataSource source = new EncryptionDecorator(new FileDataSource());
source.write("Hello"); // 自动加密写入

PHP 实现

php
// 组件接口
interface DataSource {
    public function write(string $data);
    public function read(): string;
}

// 具体组件
class FileDataSource implements DataSource {
    public function write(string $data) { /* 写入文件 */ }
    public function read(): string { return ""; }
}

// 装饰器基类
abstract class DataSourceDecorator implements DataSource {
    protected $wrappee;
    public function __construct(DataSource $source) {
        $this->wrappee = $source;
    }
}

// 压缩装饰器
class CompressionDecorator extends DataSourceDecorator {
    public function write(string $data) {
        $this->wrappee->write($this->compress($data));
    }
    private function compress(string $data): string {
        return "COMPRESSED:" . $data;
    }
}

// 客户端
$source = new CompressionDecorator(new FileDataSource());
$source->write("Hello"); // 自动压缩写入

应用场景

  1. 动态功能扩展:运行时添加日志/缓存/验证
  2. 流式处理:数据管道(加密→压缩→传输)
  3. 中间件系统:HTTP请求处理(身份验证→日志→路由)
  4. UI组件增强:带滚动条/边框的基础文本框

案例:咖啡订单系统

Java 实现

java
// 组件接口
interface Coffee {
    double getCost();
    String getDescription();
}

// 具体组件
class SimpleCoffee implements Coffee {
    public double getCost() { return 2.0; }
    public String getDescription() { return "Simple Coffee"; }
}

// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; }
}

// 牛奶装饰器
class MilkDecorator extends CoffeeDecorator {
    MilkDecorator(Coffee coffee) { super(coffee); }
    public double getCost() { return decoratedCoffee.getCost() + 0.5; }
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }
}

// 客户端
Coffee coffee = new MilkDecorator(new SimpleCoffee());
System.out.println(coffee.getDescription()); // "Simple Coffee, Milk"

PHP 实现

php
// 组件接口
interface Coffee {
    public function getCost(): float;
    public function getDescription(): string;
}

// 具体组件
class SimpleCoffee implements Coffee {
    public function getCost(): float { return 2.0; }
    public function getDescription(): string { return "Simple Coffee"; }
}

// 糖装饰器
class SugarDecorator implements Coffee {
    protected $coffee;
    public function __construct(Coffee $coffee) {
        $this->coffee = $coffee;
    }
    public function getCost(): float {
        return $this->coffee->getCost() + 0.3;
    }
    public function getDescription(): string {
        return $this->coffee->getDescription() . ", Sugar";
    }
}

// 客户端
$coffee = new SugarDecorator(new SimpleCoffee());
echo $coffee->getDescription(); // "Simple Coffee, Sugar"

优点

  1. 开闭原则:新增功能无需修改原有代码
  2. 动态组合:运行时自由叠加装饰器(如加密+压缩
  3. 避免继承爆炸:用组合替代多层次继承
  4. 职责分离:核心功能与附加功能解耦

缺点

  1. 小对象激增:多层装饰产生大量小对象
  2. 调试困难:嵌套调用链增加栈深度(如装饰器A→B→C
  3. 初始化复杂:需逐层包装对象
  4. 接口限制:装饰器必须完全实现组件接口

扩展

  1. 装饰器链
    • 支持有序装饰器(如先压缩后加密)
  2. 默认实现
    • 装饰器基类提供空方法,子类按需重写
  3. 撤销机制
    • 记录装饰历史,支持功能回退

模式协作

  • 与适配器模式:适配器改变接口,装饰器增强功能
  • 与组合模式:装饰器可包装组合对象(如带样式的UI容器)
  • 与责任链模式:装饰链类似责任链,但目的不同(增强 vs 传递)

延伸思考

  1. 函数式实现
    • JavaScript高阶函数:const logDecorator = fn => (...args) => { console.log(args); return fn(...args); }
  2. AOP替代方案
    • Spring AOP通过代理实现装饰器效果
  3. 不可变装饰
    • 函数式编程中,装饰器返回新对象而非修改原对象

总结

装饰器模式通过嵌套包装对象实现功能的动态扩展,完美平衡了灵活性与可维护性。其核心价值在于:以组合替代继承,用装饰分离关注点。在IO流处理、中间件系统等场景中,装饰器模式能显著降低代码耦合度,是应对功能扩展需求的优雅解决方案。