装饰器模式
引言
在软件设计中,常需为对象动态添加功能(如日志记录、加密)。传统继承会导致类爆炸,且破坏开闭原则。装饰器模式通过嵌套包装对象的方式,在不修改原有结构的前提下,实现功能的灵活扩展,成为增强对象行为的首选方案。
诞生背景
GoF在《设计模式》中提出装饰器模式,解决以下痛点:
- 继承僵化:子类组合爆炸(如
File+Encrypt+Compress需多重继承) - 功能耦合:核心逻辑与附加功能混杂(如支付接口包含日志/验证代码)
- 动态扩展难:运行时无法自由增减功能
演进过程
- 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"); // 自动压缩写入应用场景
- 动态功能扩展:运行时添加日志/缓存/验证
- 流式处理:数据管道(加密→压缩→传输)
- 中间件系统:HTTP请求处理(身份验证→日志→路由)
- 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"优点
- 开闭原则:新增功能无需修改原有代码
- 动态组合:运行时自由叠加装饰器(如
加密+压缩) - 避免继承爆炸:用组合替代多层次继承
- 职责分离:核心功能与附加功能解耦
缺点
- 小对象激增:多层装饰产生大量小对象
- 调试困难:嵌套调用链增加栈深度(如
装饰器A→B→C) - 初始化复杂:需逐层包装对象
- 接口限制:装饰器必须完全实现组件接口
扩展
- 装饰器链:
- 支持有序装饰器(如先压缩后加密)
- 默认实现:
- 装饰器基类提供空方法,子类按需重写
- 撤销机制:
- 记录装饰历史,支持功能回退
模式协作
- 与适配器模式:适配器改变接口,装饰器增强功能
- 与组合模式:装饰器可包装组合对象(如带样式的UI容器)
- 与责任链模式:装饰链类似责任链,但目的不同(增强 vs 传递)
延伸思考
- 函数式实现:
- JavaScript高阶函数:
const logDecorator = fn => (...args) => { console.log(args); return fn(...args); }
- JavaScript高阶函数:
- AOP替代方案:
- Spring AOP通过代理实现装饰器效果
- 不可变装饰:
- 函数式编程中,装饰器返回新对象而非修改原对象
总结
装饰器模式通过嵌套包装对象实现功能的动态扩展,完美平衡了灵活性与可维护性。其核心价值在于:以组合替代继承,用装饰分离关注点。在IO流处理、中间件系统等场景中,装饰器模式能显著降低代码耦合度,是应对功能扩展需求的优雅解决方案。