在Java开发中,装饰器模式是一种常用的结构型设计模式,核心价值在于“动态增强对象功能”,同时遵循开闭原则——不用修改原有代码,也能灵活扩展功能,避免了继承带来的类爆炸问题。
简单来说,装饰器模式就像给对象“穿衣服”:原始对象是基础衣物,每个装饰器都是一件“功能外套”,可以按需给原始对象套上一件或多件外套,实现功能的叠加,且外套之间可以自由组合、灵活拆卸。
不同于其他设计模式的抽象晦涩,装饰器模式贴近实际开发场景,比如日志增强、缓存处理、权限校验等场景,都能用到它。今天我们就以最常见的“日志增强”为例,一步步带大家理解和落地装饰器模式。
一、核心场景
我们系统里有一个基础的日志记录功能,只能打印控制台日志。现在需要扩展两个新需求:
1. 给日志加上时间戳;
2. 把重要日志同步到钉钉通知。
这两个需求是可选的,可能单独用,也可能一起用,用装饰器模式来实现再合适不过。
一、定义核心接口和基础实现

装饰器模式的核心是“接口一致”,所以先定义一个日志接口,明确核心功能——记录日志。不管是基础日志,还是后面加的增强功能,都要实现这个接口,保证结构统一。
然后是基础实现类,也就是我们最开始的控制台日志,这是被装饰的“原始对象”,只实现最基础的功能,不掺杂任何扩展逻辑,保证单一职责。
到这里,基础的日志功能就完成了。现在如果直接用这个类,只能在控制台打印日志。接下来,我们用装饰器来扩展时间戳和钉钉通知功能,全程不修改这个基础类。
二、实现装饰器基类:统一装饰器结构
装饰器需要“包裹”原始对象,同时具备和原始对象一致的接口(这样才能无缝替换)。所以我们先写一个装饰器基类,实现LogService接口,并且持有一个LogService的引用(用来指向被装饰的对象,可能是原始对象,也可能是其他装饰器)。
这个基类不用实现具体的增强逻辑,只是做一个统一的封装,让所有具体装饰器都继承它,减少重复代码。
这里有个关键:用组合(持有引用)代替继承,这也是装饰器模式比单纯继承灵活的核心原因。继承是静态的,一旦定死就不能改;而组合是动态的,我们可以在运行时给对象套不同的装饰器。
三、实现具体装饰器:添加实际增强功能

接下来就是实战重点,写两个具体的装饰器,分别实现“时间戳增强”和“钉钉通知增强”。每个装饰器只负责一个增强功能,符合单一职责原则,也方便后续灵活组合。
3.1 时间戳装饰器:给日志加时间
这个装饰器的功能很简单:在原始日志的基础上,加上当前系统时间戳,不改变原始的控制台打印逻辑。
3.2 钉钉通知装饰器:同步日志到钉钉
这个装饰器的功能是:在打印控制台日志的同时,把ERROR级别的日志同步发送到钉钉群(实际开发中,这里可以替换成真实的钉钉API调用,这里简化为打印提示)。
装饰器的执行顺序可以灵活控制。比如上面的钉钉装饰器,是先执行原始日志打印,再发送钉钉;如果我们想先校验日志内容,再打印,只需要调整代码顺序即可。
四、测试使用:灵活组合装饰器
装饰器模式的优势的在于“灵活组合”,我们可以根据需求,给原始对象套不同的装饰器,实现不同的功能组合,而且不用修改任何已有代码。
写一个测试类,看看三种场景的效果:只打印控制台日志、打印日志+时间戳、打印日志+时间戳+钉钉通知。
测试结果(实际运行输出):
从测试结果能看出来,我们通过不同的装饰器组合,实现了三种不同的日志功能,而且原始的ConsoleLogService类没有做任何修改,完全符合开闭原则。
五、注意点接口一致性:装饰器和被装饰对象必须实现同一个接口(或继承同一个抽象类),否则无法无缝替换,这是装饰器模式的前提。装饰顺序敏感:多个装饰器组合时,顺序会影响结果。比如“先加时间戳,再发送钉钉”和“先发送钉钉,再加时间戳”,钉钉收到的日志内容会不一样,实际使用时要明确顺序。避免过度设计:如果只是简单的功能扩展,比如只有一个增强需求,直接修改代码(或用继承)可能更简单,没必要硬上装饰器。装饰器适合“多维度、可组合”的增强场景。六、总结
装饰器模式的核心,就是“用组合代替继承,动态增强对象功能”。它不用修改原有代码,就能灵活扩展,还能实现功能的自由组合,特别适合多维度、可选的增强场景(比如日志、缓存、权限校验等)。




