跳到主要内容

装饰器模式(Decorator)

装饰器模式是一种结构型设计模式,它允许在不改变对象结构的情况下,动态地给对象添加额外的职责。相比继承,装饰器模式提供了更加灵活的扩展方式。

模式定义

装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。就扩展功能而言,装饰器模式提供了比继承更有弹性的替代方案。

核心要点

  1. 动态扩展:在运行时动态地给对象添加功能
  2. 透明性:装饰后的对象与原对象具有相同的接口
  3. 组合优于继承:通过组合而非继承来扩展功能
  4. 单一职责:每个装饰器只负责一个功能

问题场景

假设我们需要实现一个咖啡订单系统,顾客可以选择不同种类的咖啡,并添加各种调料(牛奶、糖、巧克力等):

// 问题代码:使用继承导致类爆炸
public abstract class Beverage {
protected String description = "未知饮料";

public String getDescription() {
return description;
}

public abstract double cost();
}

// 各种咖啡
public class Espresso extends Beverage {
public Espresso() {
description = "浓缩咖啡";
}

public double cost() {
return 1.99;
}
}

// 各种调料组合导致类爆炸
public class EspressoWithMilk extends Beverage { ... }
public class EspressoWithMilkAndMocha extends Beverage { ... }
public class EspressoWithMilkAndMochaAndWhip extends Beverage { ... }
// 无数种组合...

存在的问题

  • 类的数量随调料组合呈指数增长
  • 修改调料价格需要修改多个类
  • 无法动态添加或移除调料

解决方案

使用装饰器模式,将调料作为装饰器包装咖啡对象:

// 饮料基类
public abstract class Beverage {
protected String description = "未知饮料";

public String getDescription() {
return description;
}

public abstract double cost();
}

// 装饰器基类
public abstract class CondimentDecorator extends Beverage {
protected Beverage beverage;

public abstract String getDescription();
}

// 具体饮料:浓缩咖啡
public class Espresso extends Beverage {
public Espresso() {
description = "浓缩咖啡";
}

public double cost() {
return 1.99;
}
}

// 具体饮料:深焙咖啡
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "深焙咖啡";
}

public double cost() {
return 0.99;
}
}

// 装饰器:牛奶
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
this.beverage = beverage;
}

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

public double cost() {
return beverage.cost() + 0.10;
}
}

// 装饰器:摩卡
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}

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

public double cost() {
return beverage.cost() + 0.20;
}
}

// 装饰器:奶泡
public class Whip extends CondimentDecorator {
public Whip(Beverage beverage) {
this.beverage = beverage;
}

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

public double cost() {
return beverage.cost() + 0.10;
}
}

// 使用示例
public class CoffeeShop {
public static void main(String[] args) {
// 点一杯浓缩咖啡
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" + beverage.cost());

// 点一杯深焙咖啡,加双份摩卡和奶泡
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}

输出

浓缩咖啡 $1.99
深焙咖啡, 摩卡, 摩卡, 奶泡 $1.49

模式结构

组成部分

  • Component(组件接口):定义对象的接口,可以给这些对象动态添加职责
  • ConcreteComponent(具体组件):定义一个具体的对象,可以给它添加职责
  • Decorator(装饰器基类):持有一个组件引用,并实现组件接口
  • ConcreteDecorator(具体装饰器):向组件添加具体的职责

实现方式

1. 基本实现

// 组件接口
public interface Component {
void operation();
}

// 具体组件
public class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("执行基本操作");
}
}

// 装饰器基类
public abstract class Decorator implements Component {
protected Component component;

public Decorator(Component component) {
this.component = component;
}

@Override
public void operation() {
component.operation();
}
}

// 具体装饰器 A
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}

@Override
public void operation() {
System.out.println("装饰器 A - 前置处理");
super.operation();
System.out.println("装饰器 A - 后置处理");
}
}

// 具体装饰器 B
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}

@Override
public void operation() {
System.out.println("装饰器 B - 前置处理");
super.operation();
System.out.println("装饰器 B - 后置处理");
}
}

// 使用示例
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.operation();
}
}

输出

装饰器 B - 前置处理
装饰器 A - 前置处理
执行基本操作
装饰器 A - 后置处理
装饰器 B - 后置处理

2. 透明装饰器

装饰器对客户端透明,客户端不需要知道对象是否被装饰:

// 数据源接口
public interface DataSource {
void writeData(String data);
String readData();
}

// 基本实现:文件数据源
public class FileDataSource implements DataSource {
private String filename;

public FileDataSource(String filename) {
this.filename = filename;
}

@Override
public void writeData(String data) {
System.out.println("写入文件: " + data);
}

@Override
public String readData() {
System.out.println("读取文件");
return "文件内容";
}
}

// 装饰器:加密
public class EncryptionDecorator implements DataSource {
private DataSource wrappee;

public EncryptionDecorator(DataSource source) {
this.wrappee = source;
}

@Override
public void writeData(String data) {
wrappee.writeData(encrypt(data));
}

@Override
public String readData() {
return decrypt(wrappee.readData());
}

private String encrypt(String data) {
return "加密(" + data + ")";
}

private String decrypt(String data) {
return data.replace("加密(", "").replace(")", "");
}
}

// 装饰器:压缩
public class CompressionDecorator implements DataSource {
private DataSource wrappee;

public CompressionDecorator(DataSource source) {
this.wrappee = source;
}

@Override
public void writeData(String data) {
wrappee.writeData(compress(data));
}

@Override
public String readData() {
return decompress(wrappee.readData());
}

private String compress(String data) {
return "压缩(" + data + ")";
}

private String decompress(String data) {
return data.replace("压缩(", "").replace(")", "");
}
}

// 使用示例
public class Client {
public static void main(String[] args) {
DataSource source = new FileDataSource("data.txt");
source.writeData("原始数据");

// 添加加密和压缩
DataSource encoded = new CompressionDecorator(
new EncryptionDecorator(new FileDataSource("data.txt"))
);
encoded.writeData("敏感数据");
}
}

实际应用案例

1. Java I/O 流

Java I/O 是装饰器模式的经典应用,各种流可以相互包装:

// 基本流
InputStream inputStream = new FileInputStream("file.txt");

// 添加缓冲功能
InputStream bufferedStream = new BufferedInputStream(inputStream);

// 添加数据读取功能
DataInputStream dataStream = new DataInputStream(bufferedStream);

// 一行代码完成多层装饰
InputStream stream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("file.txt")
)
);

Java I/O 装饰器类图

2. Spring 缓存装饰器

Spring 的缓存抽象使用装饰器模式:

@Service
public class UserService {
@Cacheable("users")
public User getUserById(Long id) {
// 模拟数据库查询
return userRepository.findById(id);
}
}

// Spring 内部使用装饰器添加缓存功能
public class CachingUserService implements UserService {
private UserService delegate;
private CacheManager cacheManager;

public CachingUserService(UserService delegate, CacheManager cacheManager) {
this.delegate = delegate;
this.cacheManager = cacheManager;
}

@Override
public User getUserById(Long id) {
Cache cache = cacheManager.getCache("users");
User cached = cache.get(id, User.class);
if (cached != null) {
return cached;
}
User user = delegate.getUserById(id);
cache.put(id, user);
return user;
}
}

3. Servlet 过滤器

Servlet 过滤器链是装饰器模式的变体:

public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("请求开始: " + ((HttpServletRequest) request).getRequestURI());

long start = System.currentTimeMillis();

// 调用下一个过滤器或目标资源
chain.doFilter(request, response);

long end = System.currentTimeMillis();
System.out.println("请求结束,耗时: " + (end - start) + "ms");
}
}

public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String token = httpRequest.getHeader("Authorization");

if (token == null || !validateToken(token)) {
((HttpServletResponse) response).sendError(401, "未授权");
return;
}

chain.doFilter(request, response);
}

private boolean validateToken(String token) {
return token.startsWith("Bearer ");
}
}

4. 日志装饰器

为服务添加日志功能:

// 服务接口
public interface UserService {
User getUser(Long id);
void saveUser(User user);
}

// 基本实现
public class UserServiceImpl implements UserService {
@Override
public User getUser(Long id) {
return new User(id, "张三");
}

@Override
public void saveUser(User user) {
System.out.println("保存用户: " + user);
}
}

// 日志装饰器
public class LoggingUserServiceDecorator implements UserService {
private UserService delegate;
private Logger logger = LoggerFactory.getLogger(getClass());

public LoggingUserServiceDecorator(UserService delegate) {
this.delegate = delegate;
}

@Override
public User getUser(Long id) {
logger.info("获取用户,ID: {}", id);
try {
User user = delegate.getUser(id);
logger.info("获取用户成功: {}", user);
return user;
} catch (Exception e) {
logger.error("获取用户失败", e);
throw e;
}
}

@Override
public void saveUser(User user) {
logger.info("保存用户: {}", user);
try {
delegate.saveUser(user);
logger.info("保存用户成功");
} catch (Exception e) {
logger.error("保存用户失败", e);
throw e;
}
}
}

// 使用
UserService service = new LoggingUserServiceDecorator(new UserServiceImpl());

装饰器模式 vs 继承

特性装饰器模式继承
扩展时机运行时动态扩展编译时静态扩展
扩展数量可无限组合类爆炸
灵活性高,可动态添加移除低,需修改代码
复杂度较高较低
性能略低(多层调用)较高

装饰器模式 vs 代理模式

特性装饰器模式代理模式
目的添加功能控制访问
对象创建客户端控制代理控制
关注点功能增强访问控制
透明性完全透明可能不透明

优缺点分析

优点

  1. 开闭原则:无需修改现有代码即可扩展功能
  2. 单一职责原则:每个装饰器只负责一个功能
  3. 动态组合:可以在运行时组合不同的装饰器
  4. 避免类爆炸:用组合代替继承,减少类的数量

缺点

  1. 增加复杂性:引入多个小类
  2. 调试困难:多层装饰可能导致调试困难
  3. 初始化复杂:需要正确组合装饰器
  4. 性能开销:多层调用有轻微性能开销

使用建议

何时使用装饰器

  • 需要在运行时动态地给对象添加功能
  • 需要组合多种功能,且组合方式多变
  • 不能通过继承来扩展功能(如 final 类)
  • 需要避免类爆炸问题

何时避免使用装饰器

  • 功能扩展简单,继承更直观
  • 不需要动态组合功能
  • 装饰器会导致系统过于复杂

最佳实践

  1. 保持装饰器简单:每个装饰器只做一件事
  2. 保持接口一致:装饰器与组件接口保持一致
  3. 注意装饰顺序:装饰顺序可能影响结果
  4. 提供便捷方法:提供工厂方法简化装饰器创建

小结

装饰器模式是一种灵活的功能扩展方式:

应用场景示例
动态添加功能咖啡调料系统
功能组合Java I/O 流
透明增强日志、缓存装饰器
避免类爆炸多种功能组合

练习

  1. 实现一个文本装饰器,支持添加行号、时间戳、大写转换等功能
  2. 实现一个简单的缓存装饰器,缓存方法返回值
  3. 分析 Java Collections.synchronizedList() 的实现
  4. 实现一个重试装饰器,在方法失败时自动重试

参考资源