装饰器模式(Decorator)
装饰器模式是一种结构型设计模式,它允许在不改变对象结构的情况下,动态地给对象添加额外的职责。相比继承,装饰器模式提供了更加灵活的扩展方式。
模式定义
装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。就扩展功能而言,装饰器模式提供了比继承更有弹性的替代方案。
核心要点
- 动态扩展:在运行时动态地给对象添加功能
- 透明性:装饰后的对象与原对象具有相同的接口
- 组合优于继承:通过组合而非继承来扩展功能
- 单一职责:每个装饰器只负责一个功能
问题场景
假设我们需要实现一个咖啡订单系统,顾客可以选择不同种类的咖啡,并添加各种调料(牛奶、糖、巧克力等):
// 问题代码:使用继承导致类爆炸
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 代理模式
| 特性 | 装饰器模式 | 代理模式 |
|---|---|---|
| 目的 | 添加功能 | 控制访问 |
| 对象创建 | 客户端控制 | 代理控制 |
| 关注点 | 功能增强 | 访问控制 |
| 透明性 | 完全透明 | 可能不透明 |
优缺点分析
优点
- 开闭原则:无需修改现有代码即可扩展功能
- 单一职责原则:每个装饰器只负责一个功能
- 动态组合:可以在运行时组合不同的装饰器
- 避免类爆炸:用组合代替继承,减少类的数量
缺点
- 增加复杂性:引入多个小类
- 调试困难:多层装饰可能导致调试困难
- 初始化复杂:需要正确组合装饰器
- 性能开销:多层调用有轻微性能开销
使用建议
何时使用装饰器
- 需要在运行时动态地给对象添加功能
- 需要组合多种功能,且组合方式多变
- 不能通过继承来扩展功能(如 final 类)
- 需要避免类爆炸问题
何时避免使用装饰器
- 功能扩展简单,继承更直观
- 不需要动态组合功能
- 装饰器会导致系统过于复杂
最佳实践
- 保持装饰器简单:每个装饰器只做一件事
- 保持接口一致:装饰器与组件接口保持一致
- 注意装饰顺序:装饰顺序可能影响结果
- 提供便捷方法:提供工厂方法简化装饰器创建
小结
装饰器模式是一种灵活的功能扩展方式:
| 应用场景 | 示例 |
|---|---|
| 动态添加功能 | 咖啡调料系统 |
| 功能组合 | Java I/O 流 |
| 透明增强 | 日志、缓存装饰器 |
| 避免类爆炸 | 多种功能组合 |
练习
- 实现一个文本装饰器,支持添加行号、时间戳、大写转换等功能
- 实现一个简单的缓存装饰器,缓存方法返回值
- 分析 Java
Collections.synchronizedList()的实现 - 实现一个重试装饰器,在方法失败时自动重试