装饰器模式(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 是最经典落地案例。

终极口诀:不改原码、不生子类、层层包装、动态增强。

更多推荐