装饰器模式
装饰器模式(Decorator Pattern)是 GoF 结构性设计模式,核心作用是在不修改原有对象结构、不产生子类爆炸的前提下,动态、灵活地给对象叠加额外功能。相比继承的静态拓展,装饰器属于组合式动态拓展,完美满足开闭原则,是开发中高频使用的轻量化增强方案。
核心口诀:继承静态加功能,装饰动态套功能;层层包装、层层增强,原类代码零改动。
一、模式核心概述
1.1 核心定义
动态地将责任(功能)附加到单个对象上,无需修改原类代码,也无需通过大量子类继承拓展。通过包装+组合的方式,灵活叠加、撤销、组合拓展功能,实现原有核心功能与拓展功能的解耦。
1.2 解决的核心痛点
-
继承滥用问题:传统继承拓展功能会导致子类爆炸,功能组合越多,子类数量指数级增长,维护成本极高。
-
代码侵入问题:直接修改原类新增功能,违背开闭原则,会污染核心业务代码,影响原有逻辑稳定性。
-
功能固化问题:继承拓展的功能是静态绑定的,无法按需动态叠加、动态撤销,灵活性极差。
1.3 核心设计思想
优先组合、摒弃继承:通过装饰器持有被装饰对象的引用,调用原对象方法的同时叠加拓展逻辑,实现原功能保留、新功能叠加、动态可组合的效果。
二、核心角色与UML结构
2.1 四大核心角色
-
抽象组件(Component):顶层抽象接口/抽象类,定义所有被装饰对象和装饰器的统一行为规范,保证装饰器与原对象接口一致,支持层层包装。
-
具体组件(ConcreteComponent):原始核心业务类,实现基础核心功能,是被装饰的目标对象,核心代码完全无需修改。
-
抽象装饰器(Decorator):继承/实现抽象组件,内部持有抽象组件的引用,统一包装逻辑,是所有具体装饰器的父类,负责衔接原对象与拓展功能。
-
具体装饰器(ConcreteDecorator):继承抽象装饰器,重写业务方法,在原方法执行前后叠加自定义拓展功能,可无限新增、自由组合。
2.2 结构核心特征
装饰器与被装饰对象同源接口,保证装饰器可以无限嵌套包装,支持多装饰器自由组合,实现功能的叠加增强,且完全不侵入原对象代码。
装饰器模式 标准UML类关联图
UML图核心关联解读:顶层Component统一所有组件与装饰器的行为接口;ConcreteComponent为原始业务对象,实现基础功能;Decorator抽象装饰器实现Component接口,内部持有组件对象,实现包装能力统一;ConcreteDecorator具体装饰器继承抽象装饰器,重写方法完成动态功能增强,依托组合关系实现多层嵌套装饰。

三、落地代码实战(Java标准实现)
业务场景:基础咖啡为核心产品,可动态叠加牛奶、糖、巧克力等配料,每种配料独立装饰、自由组合,无需新增大量子类。
3.1 抽象组件(咖啡统一规范)
// 抽象组件:咖啡接口
public interface Coffee {
// 获取价格
double getPrice();
// 获取咖啡描述
String getDesc();
}
3.2 具体组件(原始基础对象)
// 具体组件:基础黑咖啡(核心原始对象)
public class OriginalCoffee implements Coffee {
@Override
public double getPrice() {
return 10.0;
}
@Override
public String getDesc() {
return "基础黑咖啡";
}
}
3.3 抽象装饰器
// 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {
// 持有被装饰的咖啡对象(核心:组合优于继承)
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
3.4 具体装饰器(多拓展功能)
// 具体装饰器:加牛奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getPrice() {
// 原有价格 + 牛奶加价
return coffee.getPrice() + 3.0;
}
@Override
public String getDesc() {
return coffee.getDesc() + " + 牛奶";
}
}
// 具体装饰器:加糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getPrice() {
return coffee.getPrice() + 1.0;
}
@Override
public String getDesc() {
return coffee.getDesc() + " + 糖";
}
}
// 具体装饰器:加巧克力
public class ChocolateDecorator extends CoffeeDecorator {
public ChocolateDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getPrice() {
return coffee.getPrice() + 5.0;
}
@Override
public String getDesc() {
return coffee.getDesc() + " + 巧克力";
}
}
3.5 测试调用(动态嵌套装饰)
public class DecoratorTest {
public static void main(String[] args) {
// 1. 基础咖啡
Coffee coffee = new OriginalCoffee();
System.out.println(coffee.getDesc() + " 价格:" + coffee.getPrice());
// 2. 动态叠加装饰:黑咖啡 + 牛奶 + 糖
Coffee milkSugarCoffee = new SugarDecorator(new MilkDecorator(new OriginalCoffee()));
System.out.println(milkSugarCoffee.getDesc() + " 价格:" + milkSugarCoffee.getPrice());
// 3. 多层嵌套:黑咖啡 + 牛奶 + 糖 + 巧克力
Coffee fullCoffee = new ChocolateDecorator(milkSugarCoffee);
System.out.println(fullCoffee.getDesc() + " 价格:" + fullCoffee.getPrice());
}
}
3.6 代码核心特点
-
新增配料无需修改原有咖啡类、原有装饰器,完全符合开闭原则
-
支持任意嵌套、任意组合,功能拓展极度灵活
-
核心业务代码与拓展代码完全解耦,职责单一
四、优缺点深度分析
4.1 优点
-
完美遵循开闭原则:拓展功能无需修改原对象代码,仅新增装饰器即可,无代码侵入。
-
彻底解决子类爆炸:摒弃静态继承,通过动态组合实现功能叠加,无限拓展无冗余类。
-
功能灵活可控:支持按需叠加、自由组合、动态撤销,比继承、静态重写灵活度更高。
-
职责单一清晰:原类负责核心功能,各装饰器负责单一拓展功能,解耦彻底。
4.2 缺点
-
类数量小幅增加:每一个独立拓展功能需要新增一个具体装饰器类,会产生少量细粒度类。
-
嵌套层级复杂:多层装饰嵌套后,代码阅读、调试链路变长,排查问题成本略高。
-
场景局限性:仅适用于功能叠加增强场景,不适合核心逻辑修改、功能替换场景。
五、适用场景与落地场景
5.1 核心适用场景
-
需要动态、灵活地为对象叠加拓展功能,且不允许修改原类代码
-
功能组合繁多,使用继承会导致大量子类爆炸的场景
-
需要随时新增、移除、组合拓展功能的业务场景
-
核心功能稳定、拓展功能频繁迭代的场景
5.2 经典源码落地场景
-
IO流体系:Java IO 是装饰器模式经典落地,FileReader(原始组件)、BufferedReader(装饰器)、InputStreamReader(装饰器)层层包装增强。
-
Spring 缓存、请求包装:HttpServletRequestWrapper 采用装饰器模式,动态增强请求参数处理能力。
-
MyBatis 插件拦截:通过装饰包装 Executor、StatementHandler,动态增强 SQL 执行逻辑。
六、高频面试核心考点
6.1 装饰器 vs 继承
-
继承:静态拓展、编译期绑定、代码侵入、子类爆炸、无法动态组合,违背开闭原则拓展。
-
装饰器:动态拓展、运行期绑定、无侵入、组合复用、自由嵌套组合,遵循开闭原则。
6.2 装饰器模式核心本质
同接口、持原对象、嵌套包装、动态增强。不改变原对象结构,只增强功能。
6.3 装饰器与适配器模式区别
-
装饰器:接口不变,增强原有功能,同源接口嵌套包装。
-
适配器:接口转换,适配不同接口,解决接口不兼容问题,不负责功能增强。
七、面试速记总结
装饰器模式是结构性模式,核心是组合替代继承、动态叠加功能。通过抽象组件统一规范,抽象装饰器统一包装,具体装饰器实现功能拓展,可无限嵌套、自由组合。解决了继承带来的子类爆炸、功能固化、代码侵入问题,适用于核心功能稳定、拓展功能灵活迭代的场景,Java IO 是最经典落地案例。
终极口诀:不改原码、不生子类、层层包装、动态增强。
更多推荐


所有评论(0)