装饰器模式 (Decorator Pattern)
动态地为对象添加额外职责,比继承更灵活
目录
概述
装饰器模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊包装对象中来为原对象绑定新的行为。
适用场景
- 在不修改原类的情况下添加功能
- 需要动态组合功能
- 替代多层继承
基础实现
// 组件接口
public interface Coffee {
double cost();
String description();
}
// 具体组件
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 10;
}
@Override
public String description() {
return "简单咖啡";
}
}
// 装饰器基类
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double cost() {
return coffee.cost();
}
@Override
public String description() {
return coffee.description();
}
}
// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 2;
}
@Override
public String description() {
return super.description() + ", 牛奶";
}
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 1;
}
@Override
public String description() {
return super.description() + ", 糖";
}
}
// 使用
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee); // 加牛奶
coffee = new SugarDecorator(coffee); // 加糖
System.out.println(coffee.description()); // 简单咖啡, 牛奶, 糖
System.out.println(coffee.cost()); // 13.0IO 流中的应用
// Java IO 是装饰器模式的经典应用
// FileInputStream 是基础组件
InputStream fileStream = new FileInputStream("file.txt");
// BufferedInputStream 是装饰器,添加缓冲功能
InputStream bufferedStream = new BufferedInputStream(fileStream);
// DataInputStream 是装饰器,添加基本数据类型读取功能
DataInputStream dataStream = new DataInputStream(bufferedStream);
// 组合多个装饰器
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("file.txt"),
"UTF-8"
)
);实际应用:权限装饰器
// 数据服务接口
public interface DataService {
String getData(String key);
void saveData(String key, String value);
}
// 基础实现
@Service
public class DataServiceImpl implements DataService {
@Override
public String getData(String key) {
return "data for " + key;
}
@Override
public void saveData(String key, String value) {
System.out.println("保存: " + key + "=" + value);
}
}
// 日志装饰器
@Component
public class LoggingDataService implements DataService {
private final DataService delegate;
public LoggingDataService(DataService delegate) {
this.delegate = delegate;
}
@Override
public String getData(String key) {
System.out.println("[LOG] 获取数据: " + key);
return delegate.getData(key);
}
@Override
public void saveData(String key, String value) {
System.out.println("[LOG] 保存数据: " + key + "=" + value);
delegate.saveData(key, value);
}
}
// 缓存装饰器
@Component
public class CachingDataService implements DataService {
private final DataService delegate;
private final Map<String, String> cache = new ConcurrentHashMap<>();
public CachingDataService(DataService delegate) {
this.delegate = delegate;
}
@Override
public String getData(String key) {
return cache.computeIfAbsent(key, delegate::getData);
}
@Override
public void saveData(String key, String value) {
delegate.saveData(key, value);
cache.put(key, value);
}
}
// 组合使用
coffee = new SimpleCoffee();
coffee = new CachingDataService(coffee);
coffee = new LoggingDataService(coffee);Spring Cache 装饰器
@Service
public class UserService {
// @Cacheable 就是装饰器模式的注解实现
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) {
// 实际查询数据库
return userRepository.findById(id).orElse(null);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userRepository.save(user);
}
}Servlet Filter 链
// Servlet Filter 是装饰器模式的典型应用
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
// 前置处理:认证
if (isAuthenticated(request)) {
chain.doFilter(request, response); // 调用下一个装饰器
} else {
// 未认证,返回错误
}
// 后置处理
}
}
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
// 记录请求
chain.doFilter(request, response);
// 记录响应
}
}对比继承
| 继承 | 装饰器 |
|---|---|
| 静态绑定 | 动态绑定 |
| 编译时确定 | 运行时确定 |
| 类爆炸问题 | 灵活组合 |
| 难以撤销 | 可以动态添加/移除 |
优缺点
| 优点 | 缺点 |
|---|---|
| 比继承更灵活 | 产生大量小对象 |
| 运行时扩展功能 | 调试较复杂 |
| 符合开闭原则 |
总结
装饰器模式提供了一种灵活的替代继承的方式。在 Java IO、Spring 缓存、Servlet Filter 等框架中都有广泛应用。当需要动态组合功能时,装饰器模式是最佳选择。