【Java】23种设计模式——9.装饰器模式
概念
装饰器模式 ,是一种结构设计模式。 通过B类,实现A类方法的前后执行操作。类似于AOP。其核心是组合替代继承,避免因多维度扩展导致的子类爆炸问题。
核心角色
角色 | 职责 | 示例场景 |
---|---|---|
组件接口(Component) | 定义被装饰对象和装饰器的共同接口 | Coffee (咖啡基础接口) |
具体组件(ConcreteComponent) | 实现 Component ,表示基础对象 |
BlackCoffee (黑咖啡) |
装饰器抽象类(Decorator) | 实现 Component ,持有 Component 引用 |
CoffeeDecorator (装饰器基类) |
具体装饰器(ConcreteDecorator) | 继承 Decorator ,添加具体扩展功能 |
MilkDecorator (加牛奶装饰) |
应用场景
- 动态添加和撤销功能 如为对象添加日志、权限等临时功能。
- 避免多层继承的复杂结构 当子类数量爆炸时,用装饰器替代继承。
- 透明扩展对象行为 客户端无需改制装饰器的存在(如Java I/O流)。
使用
设计一个咖啡订单系统,支持动态添加咖啡配料,并动态获取总价和描述。
-
定义组件接口 (咖啡的抽象接口)
/** * 组件接口 : 咖啡 * @Author:lyj * @Date:2025/5/6 15:59 */ public interface Coffee { public double getCost(); // 获取 public String getDescription(); // 获取描述 }
-
实现具体组件 (咖啡基础:黑咖啡、美式咖啡)
/** * 具体组件 : 黑咖啡 * @Author:lyj * @Date:2025/5/6 16:02 */ public class BlackCoffee implements Coffee { /** * 获取咖啡价格 * @return */ @Override public double getCost() { return 2.0; // 基础价格 } /** * 获取咖啡描述 * @return */ @Override public String getDescription() { return "黑咖啡"; } } /** * 具体组件:美式咖啡 * @Author:lyj * @Date:2025/5/7 10:48 */ public class Americano implements Coffee{ /** * 获取咖啡价格 * @return */ @Override public double getCost() { return 2.2; // 基础价格 } /** * 获取咖啡描述 * @return */ @Override public String getDescription() { return "美式咖啡"; } }
-
定义装饰器抽象类(包装组件) (咖啡 + 配料)
/** * 装饰器抽象类(持有装饰器) : 咖啡+配料 * 持有被装饰对象的引用 * @Author:lyj * @Date:2025/5/7 10:50 */ public abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; // 被装饰的咖啡对象 public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } /** * 获取咖啡价格 * 默认直接调用被装饰对象的方法 * @return */ @Override public double getCost() { return coffee.getCost(); } @Override public String getDescription() { return coffee.getDescription(); } }
-
实现具体装饰器 (加糖+ 加奶)
/** * 具体装饰器 : 添加牛奶 * @Author:lyj * @Date:2025/5/7 14:05 */ public class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } /** * 获取价格 * @return */ @Override public double getCost() { return super.getCost() + 0.5; } @Override public String getDescription() { return super.getDescription() + "+牛奶"; } }
/** * 具体装饰器 : 添加糖 * @Author:lyj * @Date:2025/5/7 14:05 */ public class SugurDecorator extends CoffeeDecorator { public SugurDecorator(Coffee coffee) { super(coffee); } /** * 获取价格 * @return */ @Override public double getCost() { return super.getCost() + 0.4; } @Override public String getDescription() { return super.getDescription() + "+糖"; } }
测试运行
// 基础黑咖啡
Coffee coffee = new BlackCoffee();
System.out.println(coffee.getDescription() + ": " + coffee.getCost()); // 黑咖啡: 2.0
// 加上牛奶
Coffee coffee1 = new MilkDecorator(coffee);
System.out.println(coffee1.getDescription() + ": " + coffee1.getCost()); // 黑咖啡+牛奶: 2.5
// 加糖
Coffee coffee2 = new SugurDecorator(coffee1);
System.out.println(coffee2.getDescription() + ": " + coffee2.getCost()); // 黑咖啡+牛奶+糖: 2.9
// 基础美式咖啡
Coffee coffee3 = new Americano();
System.out.println(coffee3.getDescription() + ": " + coffee3.getCost()); // 美式咖啡: 2.2
// 加糖
Coffee coffee4 = new SugurDecorator(coffee3);
System.out.println(coffee4.getDescription() + ": " + coffee4.getCost()); // 美式咖啡+糖: 2.6
// 加牛奶
Coffee coffee5 = new MilkDecorator(coffee4);
System.out.println(coffee5.getDescription() + ": " + coffee5.getCost()); // 美式咖啡+糖+牛奶: 3.1
Java 标准库中的应用装饰器
Java I/O流失装饰器的经典案例
// 基础组件:FileInputStream
InputStream in = new FileInputStream("test.txt");
// 装饰器:BufferedInputStream(添加缓冲功能)
InputStream bufferedIn = new BufferedInputStream(in);
// 装饰器:DataInputStream(添加数据类型读取功能)
InputStream dataIn = new DataInputStream(bufferedIn);
装饰器模式和注解模式区别
注解的使用,可查看:https://www.cnblogs.com/luyj00436/p/18544203
对比点 | 装饰器模式 | 注解模式 |
---|---|---|
本质 | 行为增强工具 | 元数据标记工具 |
执行主体 | 自身实现逻辑 | 依赖外部处理器 |
扩展维度 | 功能叠加(横向扩展) | 配置标记(纵向解耦) |
适用选择建议:
- 需要动态叠加功能(如日志、缓存)→优先使用装饰器模式
- 需要标记配置信息(如权限、路由)→优先使用注解模式。
装饰器模式使用场景例子
- Java I/O 流增强 。
BufferedInputStream
包装FileInputStream
添加缓冲功能。 - UI 组件扩展。 动态为GUI添加边框、阴影等视觉效果。
- 电商促销叠加。 基础价格通过
DiscountDecorator
叠加满减、优惠券等促销规则。 - 日志记录增强 。 在和兴业务逻辑外层包装日记装饰器,记录方法时间。
- 权限校验链 。 多层装饰器实现多重校验(角色校验+IP白名单)
- 缓存机制扩展。 为数据库查询添加
CacheDecorator
实现结果缓存。 - 数据压缩处理。 使用
GzipDecorator
包装网络传输流实现自动压缩。 - 加密解密扩展。 在数据库存储前通过
EncrypDecorator
自动加密敏感字段。 - 消息格式转换。 为消息处理添加
XMLFormatterDecorator
实现格式转换。 - 游戏角色装备系统 。 动态为角色添加武器、护甲等装备效果(属性值动态计算)。
注解模式使用场景
元数据驱动场景
- 依赖注入标记。 Spring 的
@Autowired
自动注入 Bean实例。 - 单元测试标识 。 JUnit的
@Test
标记测试方法。 - API文档生成。 Swagger 的
@Api
注解生成接口文档描述。 - 路由器映射控制 Spring MVC 的
@RequestMapping
,定义HTTP请求路径。 - 代码生成简化 Lomlok的
@Data
自动生成 Getter/Setter方法。 - 权限控制拦截 Spring Securify 的
@PreAuthorize
标记接口访问权限。 - 数据校验标记 JSR-303的
@Valid
校验请求参数合法性。 - JSON序列化控制 Jackson的
@JsonIgnore
忽略字段序列化。 - 定时任务调度 Spring的
@Scheduled
定义定时任务执行规则。 - ORM实体映射 Hibernate的
@Entry
标记数据库表的映射关系
有志者,事竟成,破釜沉舟,百二秦关终属楚; 苦心人,天不负,卧薪尝胆,三千越甲可吞吴。