【西瓜带你学设计模式 | 第十二期 - 装饰器模式】装饰器模式 —— 动态叠加功能实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 装饰器模式是什么?](#1. 装饰器模式是什么?)
    • [2. 装饰器模式解决什么问题?](#2. 装饰器模式解决什么问题?)
    • [3. 实现步骤](#3. 实现步骤)
    • [4. 静态结构](#4. 静态结构)
      • [4.1 抽象组件:Coffee(统一接口)](#4.1 抽象组件:Coffee(统一接口))
      • [4.2 具体组件:SimpleCoffee(基础咖啡)](#4.2 具体组件:SimpleCoffee(基础咖啡))
      • [4.3 装饰器抽象类:CoffeeDecorator](#4.3 装饰器抽象类:CoffeeDecorator)
      • [4.4 具体装饰器:Milk / Sugar](#4.4 具体装饰器:Milk / Sugar)
    • [5. 动态结构 / 运行时替换点](#5. 动态结构 / 运行时替换点)
    • [6. 优缺点](#6. 优缺点)
      • [6.1 优点](#6.1 优点)
      • [6.2 缺点](#6.2 缺点)
    • [7. 和模板方法模式对比](#7. 和模板方法模式对比)
      • [7.1 都能"增强行为",但侧重点不同](#7.1 都能“增强行为”,但侧重点不同)
      • [7.2 谁来控制流程?](#7.2 谁来控制流程?)
    • [8. 和代理模式或外观模式的区别](#8. 和代理模式或外观模式的区别)
      • [8.1 装饰器 vs 代理](#8.1 装饰器 vs 代理)
      • [8.2 装饰器 vs 外观](#8.2 装饰器 vs 外观)
    • [9. 总结](#9. 总结)

前言

在面向对象设计里,我们经常遇到这样的需求:

给一个对象"加功能",而且不想改它的类,也不想用继承把功能层层叠叠地搞爆。

这时候,装饰器模式(Decorator Pattern) 就很合适。

可以把它理解成:在不改变原对象代码的前提下,用"包装一层层"的方式给对象动态叠加职责


1. 装饰器模式是什么?

**装饰器模式:**在不改变原对象结构的情况下,通过创建一个包装类(Decorator),在运行时给对象增加额外功能。

它的核心角色一般是:

  • Component(抽象组件):定义对象的公共接口(业务能力)
  • ConcreteComponent(具体组件):真正的被装饰对象
  • Decorator(装饰器抽象类/基类) :持有一个 Component,并实现接口;用于"转发 + 增强"
  • ConcreteDecorator(具体装饰器):对某个/某类功能进行增强(例如加日志、加缓存、加权限)

2. 装饰器模式解决什么问题?

典型场景是:你想给对象增加功能,但功能组合有很多种,且希望可灵活启用/禁用

常见例子:

  1. UI 控件增强

    • 文本框 + 边框
    • 文本框 + 边框 + 水印
    • 文本框 + 边框 + 校验 + 错误提示...
  2. 请求/响应增强

    • 统一日志
    • 统一鉴权
    • 统一缓存
    • 统一压缩/序列化...
  3. 流式处理

    • 输入流 + 解密
    • 输出流 + 编码
    • 输入流 + 校验 + 解密...
  4. 电商价格/计费规则拼装

    • 基础价格 + 满减 + 优惠券 + 会员价...

3. 实现步骤

  1. 先抽象出公共接口 Component

    • 明确"对象能做什么"(核心方法签名)
  2. 写一个或多个 ConcreteComponent

    • 提供基础能力(被装饰者)
  3. 写装饰器基类 Decorator

    • 内部持有一个 Component
    • 大多数情况下,先把调用转发给被包装对象
  4. 写一个或多个 ConcreteDecorator

    • 在转发前/后做增强
    • 也可以选择不转发、改变结果(更灵活)
  5. 客户端按需"层层包裹"

    • 这一步带来装饰器模式的"动态叠加"能力

4. 静态结构

用一个"咖啡加料"例子来展示装饰器模式的静态结构与调用关系。

4.1 抽象组件:Coffee(统一接口)

java 复制代码
public interface Coffee {
    String make();
}

4.2 具体组件:SimpleCoffee(基础咖啡)

java 复制代码
public class SimpleCoffee implements Coffee {
    @Override
    public String make() {
        return "基础咖啡";
    }
}

4.3 装饰器抽象类:CoffeeDecorator

java 复制代码
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee; // 持有被装饰对象

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String make() {
        return coffee.make(); // 默认转发
    }
}

4.4 具体装饰器:Milk / Sugar

java 复制代码
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String make() {
        return coffee.make() + " + 加奶";
    }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String make() {
        return coffee.make() + " + 加糖";
    }
}

5. 动态结构 / 运行时替换点

装饰器模式真正"动态"的地方在于:

运行时决定包裹哪些装饰器,以及包裹顺序。

客户端示例:

java 复制代码
public class Client {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();

        coffee = new MilkDecorator(coffee);   // 动态叠加:加奶
        coffee = new SugarDecorator(coffee);  // 动态叠加:再加糖

        System.out.println(coffee.make());
        // 输出:基础咖啡 + 加奶 + 加糖
    }
}

你会发现:

  • SimpleCoffee 不需要改
  • MilkDecoratorSugarDecorator 都只关心"自己要增加的部分"
  • 组合方式由客户端灵活决定

6. 优缺点

6.1 优点

  • 比继承更灵活:不必为了每个组合创建大量子类
  • 可组合、可动态启用/禁用:运行时自由叠加职责
  • 符合开闭原则:新增功能通常只加新的装饰器类,不改原类

6.2 缺点

  • 装饰层级可能过多:组合复杂时,调用链会变长
  • 调试/排查问题更复杂:因为行为来自一串包装层
  • 过度使用会让结构"绕":简单场景可能不需要装饰器

7. 和模板方法模式对比

7.1 都能"增强行为",但侧重点不同

  • 模板方法(Template Method) :强调"固定流程骨架 + 子类替换步骤"
  • 装饰器(Decorator) :强调"对象被包装 + 运行时叠加职责"

可以这样记忆:

模板方法:改的是"流程里的某些步骤(继承替换)"

装饰器:改的是"对象外面加了什么功能(包装叠加)"

7.2 谁来控制流程?

  • 模板方法:父类控制流程顺序(骨架算法)
  • 装饰器:调用沿着包装链传递(谁在外层谁先/后增强,顺序可控)

8. 和代理模式或外观模式的区别

8.1 装饰器 vs 代理

两者都"包装一个对象",但意图不同:

  • 代理模式 :核心目标通常是 控制访问/管理远程/延迟加载/权限
    (比如:懒加载真实对象、权限校验、记录谁访问了它)
  • 装饰器模式 :核心目标是 增强功能/增加职责
    (比如:日志、加糖加奶、加校验、加缓存等"附加行为")

简单判断:

你包装它是为了"替你去做访问控制/转发到真实对象" → 更像代理

你包装它是为了"在同一对象能力上继续加功能" → 更像装饰器

8.2 装饰器 vs 外观

  • 外观模式:把复杂系统封装成一个简单入口("统一门面")
  • 装饰器模式:多层可组合地给单个对象"叠加职责"

9. 总结

装饰器模式的核心可以概括为一句话:

用包装(Decorator)在不修改原对象的前提下,运行时动态叠加额外职责。

它适合:

  • 功能可组合且组合种类多
  • 希望减少继承导致的爆炸
  • 希望运行时决定增强哪些能力
相关推荐
吴声子夜歌1 小时前
Node.js——zlib压缩模块
java·spring·node.js
海参崴-1 小时前
深入剖析C语言结构体存储规则:内存对齐原理与实战详解
java·c语言·开发语言
南山乐只1 小时前
Java并发工具:synchronized演进,从JDK 1.6 锁升级到 JDK 24 重构
java·开发语言·后端·职场和发展
无籽西瓜a1 小时前
【西瓜带你学设计模式 | 第十三期 - 组合模式】组合模式 —— 树形结构统一处理实现、优缺点与适用场景
java·后端·设计模式·组合模式·软件工程
翊谦10 小时前
Java Agent开发 Milvus 向量数据库安装
java·数据库·milvus
晓晓hh10 小时前
JavaSE学习——迭代器
java·开发语言·学习
查古穆10 小时前
栈-有效的括号
java·数据结构·算法
Java面试题总结10 小时前
Spring - Bean 生命周期
java·spring·rpc
硅基诗人10 小时前
每日一道面试题 10:synchronized 与 ReentrantLock 的核心区别及生产环境如何选型?
java