Java 装饰器模式Decorator详解及实现步骤

 更新时间:2025年07月17日 11:01:56   作者:magic 245  
装饰器模式通过组合动态扩展对象功能,避免继承导致的类爆炸,适用于运行时灵活添加职责的场景,广泛应用于Java I/O和Spring框架,提升代码可维护性与扩展性,本文介绍Java装饰器模式Decorator详解及实现步骤,感兴趣的朋友一起看看吧

在 Java 的世界里,设计模式是开发者手中的利器,它们帮助我们以更优雅、更灵活的方式构建软件系统。今天,我们就来深入探讨其中一种非常实用的设计模式 —— 装饰器模式(Decorator Pattern)。​

一、装饰器模式概述​

装饰器模式属于结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这就好比给一个普通的杯子加上杯盖、杯套,让它具备保温、防烫等功能,而杯子本身的基本结构并没有发生变化。在软件开发中,当我们需要在运行时动态地为对象添加职责,并且避免通过继承来扩展功能带来的类层次结构复杂问题时,装饰器模式就派上用场了。​

装饰器模式主要包含以下几个角色:​

  1. 抽象组件(Component):定义一个抽象接口,规范具体组件和装饰器的行为。​
  2. 具体组件(Concrete Component):实现抽象组件接口,是被装饰的具体对象。​
  3. 抽象装饰器(Decorator):继承或实现抽象组件接口,持有一个抽象组件的引用,并定义一个可以动态添加职责的方法。​
  4. 具体装饰器(Concrete Decorator):实现抽象装饰器接口,负责为具体组件添加具体的功能。

二、装饰器模式的实现步骤

1.故事背景:机器人功能扩展的两种方案

在智能家居领域,某科技公司推出了第一代家用机器人,它具备三个核心功能:对话、唱歌和播放音乐。

// 第一代机器人:基础功能
interface Robot {
    void talk();    // 对话
    void sing();    // 唱歌
    void playMusic(); // 播放音乐
}
class FirstGenerationRobot implements Robot {
    @Override
    public void talk() {
        System.out.println("机器人:你好,我能陪你聊天");
    }
    @Override
    public void sing() {
        System.out.println("机器人:正在播放《青花瓷》");
    }
    @Override
    public void playMusic() {
        System.out.println("机器人:正在播放轻音乐");
    }
}

随着用户需求升级,厂家希望扩展机器人的功能,让它能够扫地和跳舞。针对这个需求,有两种技术方案可供选择:

⑴.方案一:传统继承(厂家升级方案)

厂家选择开发第二代机器人,通过继承第一代产品并添加新功能:

📄 接口定义

// 基础接口:第一代机器人支持的功能
interface Robot {
    void talk();
    void sing();
    void playMusic();
}

📦 类实现

// 第一代机器人(基础实现)
class FirstGenerationRobot implements Robot {
    @Override
    public void talk() {
        System.out.println("对话中...");
    }
    @Override
    public void sing() {
        System.out.println("正在唱歌...");
    }
    @Override
    public void playMusic() {
        System.out.println("播放音乐中...");
    }
}
// 第二代机器人:通过继承方式扩展功能
class SecondGenerationRobot extends FirstGenerationRobot {
    public void sweep() {
        System.out.println("正在扫地...");
    }
    public void dance() {
        System.out.println("正在跳舞...");
    }
}

🧪 测试代码

public class InheritanceTest {
    public static void main(String[] args) {
        System.out.println("=== 继承方式测试 ===");
        SecondGenerationRobot robot = new SecondGenerationRobot();
        robot.talk();
        robot.sing();
        robot.playMusic();
        robot.sweep();  // 扩展功能
        robot.dance();   // 扩展功能
    }
}

⑵.方案二:装饰器模式

通过组合方式动态添加新功能:

以下是根据机器人例子实现的装饰器模式代码及详细解释:

抽象组件(Robot 接口)

interface Robot {
    void talk();   // 对话
    void sing();   // 唱歌
    void playMusic(); // 放音乐
}
  • 作用:定义机器人的基本功能,作为装饰器模式的核心接口,规范所有机器人(包括原始机器人和装饰后的机器人)的行为。

具体组件(第一代机器人)

class FirstGenerationRobot implements Robot {
    @Override
    public void talk() {
        System.out.println("第一代机器人:对话");
    }
    @Override
    public void sing() {
        System.out.println("第一代机器人:唱歌");
    }
    @Override
    public void playMusic() {
        System.out.println("第一代机器人:放音乐");
    }
}
  • 作用:实现抽象组件,是被装饰的原始对象(第一代机器人,具有基本功能)。

抽象装饰器(RobotDecorator)

abstract class RobotDecorator implements Robot {
    protected Robot robot; // 持有被装饰的机器人对象
    public RobotDecorator(Robot robot) {
        this.robot = robot;
    }
    // 转发原始功能(保持原有功能不变)
    @Override
    public void talk() {
        robot.talk();
    }
    @Override
    public void sing() {
        robot.sing();
    }
    @Override
    public void playMusic() {
        robot.playMusic();
    }
}
  • 作用:作为装饰器的基类,继承抽象组件接口,通过组合(关联)持有原始机器人对象。它实现了原始功能的转发,确保装饰时不破坏原有功能,同时为具体装饰器提供统一的扩展框架。

具体装饰器(添加拖地和跳舞功能)

Java 中的 super 关键字

// 拖地装饰器:继承自RobotDecorator,用于为机器人添加拖地功能
class SweepFloorDecorator extends RobotDecorator {
    // 构造函数:接收被装饰的机器人实例
    public SweepFloorDecorator(Robot robot) {
        // 通过super调用父类构造函数,保存对原始机器人的引用
        super(robot);
    }
    public void sweepFloor() { // 新增功能:拖地
        System.out.println("装饰后:拖地");
    }
}
// 跳舞装饰器
class DanceDecorator extends RobotDecorator {
    public DanceDecorator(Robot robot) {
        super(robot);
    }
    public void dance() { // 新增功能:跳舞
        System.out.println("装饰后:跳舞");
    }
}

测试代码

public class RobotDemo {
    public static void main(String[] args) {
        // 原始机器人(第一代)
        Robot robot = new FirstGenerationRobot();
        robot.talk();   // 原始功能:对话
        robot.sing();   // 原始功能:唱歌
        robot.playMusic(); // 原始功能:放音乐
        // 装饰器扩展:添加拖地功能
        SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot);
        decoratedWithSweep.talk(); // 保留原始功能
        decoratedWithSweep.sweepFloor(); // 新增功能:拖地
        // 再装饰:添加跳舞功能
        DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep);
        fullyDecorated.talk(); // 保留原始功能
        // 正确调用拖地功能
        decoratedWithSweep.sweepFloor(); // 拖地功能
        // 新增功能:跳舞
        fullyDecorated.dance();
    }
}

装饰器模式通过组合而非继承,实现了运行时动态扩展对象功能,避免了继承带来的类爆炸问题,是处理 “对象功能扩展” 的优雅方案。在机器人例子中,利用装饰器模式,无需等待厂家更新(继承方式),即可快速为第一代机器人添加拖地、跳舞功能,体现了模式的灵活性和实用性。

  • 继承机制适合功能较为稳定、扩展需求明确的场景,实现简单,但不够灵活。
  • 装饰器模式适合需要动态组合功能、避免类爆炸的情况,虽然实现略复杂,但更符合开闭原则(对扩展开放,对修改关闭)。

三、流程图和内存图

1.创建基础机器人

Robot robot = new FirstGenerationRobot();
  • 创建第一代机器人实例,具备基础功能(talk/sing/playMusic)

2.第一次装饰:添加拖地功能

SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot);
  • SweepFloorDecorator 持有对原始机器人的引用
  • 继承了 RobotDecorator 的默认实现(转发所有方法调用)

3.调用原始功能

decoratedWithSweep.talk();
  • 执行流程:
    • decoratedWithSweep.talk() 调用 RobotDecorator 的实现
    • RobotDecorator 转发给被装饰对象:robot.talk()
    • 最终执行 FirstGenerationRobot.talk()

4.调用新增功能

decoratedWithSweep.sweepFloor();
  • 直接调用 SweepFloorDecorator 新增的方法
  • 输出:"装饰后:拖地"

5.第二次装饰:添加跳舞功能

DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep);
  • DanceDecorator 持有对 SweepFloorDecorator 的引用
  • 形成装饰器嵌套结构:DanceDecorator -> SweepFloorDecorator -> FirstGenerationRobot

6.调用原始功能(多层装饰后)

fullyDecorated.talk();
  • 执行流程:
    • fullyDecorated.talk() 调用 RobotDecorator 的实现
    • 转发给被装饰对象:decoratedWithSweep.talk()
    • decoratedWithSweep.talk() 再次转发:robot.talk()
    • 最终执行 FirstGenerationRobot.talk()

7.调用第一层装饰器功能

decoratedWithSweep.sweepFloor();
  • 必须通过 decoratedWithSweep 引用调用
  • 因为 fullyDecorated 类型是 DanceDecorator,没有直接暴露 sweepFloor() 方法

8.调用第二层装饰器功能

fullyDecorated.dance();
  • 直接调用 DanceDecorator 新增的方法
  • 输出:"装饰后:跳舞"

3 个 Robot 引用的指向对象

  1. 变量 robot

    • 类型:Robot
    • 指向:FirstGenerationRobot 实例(原始机器人)
  2. SweepFloorDecorator 内部的 robot 成员变量

    • 类型:Robot(父类 RobotDecorator 的 protected Robot robot
    • 指向:FirstGenerationRobot 实例(与变量 robot 指向同一个对象)
  3. DanceDecorator 内部的 robot 成员变量

    • 类型:Robot(父类 RobotDecorator 的 protected Robot robot
    • 指向:SweepFloorDecorator 实例(与前两个引用指向不同对象)

引用的内存地址比较

Robot robot = new FirstGenerationRobot();
SweepFloorDecorator decoratedWithSweep = new SweepFloorDecorator(robot);
DanceDecorator fullyDecorated = new DanceDecorator(decoratedWithSweep);
// 内存地址比较(假设对象内存地址为示例值)
robot: 0x1000                  // 指向 FirstGenerationRobot
decoratedWithSweep.robot: 0x1000  // 指向 FirstGenerationRobot(与 robot 相同)
fullyDecorated.robot: 0x2000    // 指向 SweepFloorDecorator(与前两者不同)
  • robot 和 decoratedWithSweep.robot 指向相同对象(内存地址均为 0x1000)。
  • fullyDecorated.robot 指向不同对象(内存地址 0x2000,即 SweepFloorDecorator 实例)。

四、实战中的装饰器模式

1. Java I/O 库中的应用

// 输入流装饰示例:
InputStream fileInput = new FileInputStream("data.txt");
InputStream bufferInput = new BufferedInputStream(fileInput);
DataInputStream dataInput = new DataInputStream(bufferInput);
// 功能动态组合:
// 基础功能:FileInputStream
// 装饰1:添加缓冲功能 BufferedInputStream
// 装饰2:添加数据处理功能 DataInputStream

2. Spring 框架中的应用

// Spring Security的认证过滤器链:
FilterChain filterChain = new UsernamePasswordAuthenticationFilter(
                            new BasicAuthenticationFilter(
                                new SecurityContextPersistenceFilter()
                            )
                        );
// 每个过滤器都是一个装饰器,动态增强请求处理能力

到此这篇关于Java 装饰器模式Decorator详解及实现步骤的文章就介绍到这了,更多相关Java 装饰器模式Decorator内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis动态SQL foreach批量操作方法

    Mybatis动态SQL foreach批量操作方法

    这篇文章主要介绍了Mybatis动态SQL foreach批量操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • 详解SpringBoot定时任务说明

    详解SpringBoot定时任务说明

    本篇文章主要介绍了详解SpringBoot定时任务说明,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Java实现JDBC向数据库批量插入

    Java实现JDBC向数据库批量插入

    在Java项目中可能会出现大量向数据库中插入的情况,本文主要介绍了Java实现JDBC向数据库批量插入,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • intellij idea创建第一个动态web项目的步骤方法

    intellij idea创建第一个动态web项目的步骤方法

    这篇文章主要介绍了intellij idea创建第一个动态web项目的步骤方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • 多线程计数,怎么保持计数准确的方法

    多线程计数,怎么保持计数准确的方法

    这篇文章主要介绍了多线程计数的方法,有需要的朋友可以参考一下
    2014-01-01
  • 国内分布式框架Dubbo使用详解

    国内分布式框架Dubbo使用详解

    这篇文章主要为大家介绍了国内分布式框架Dubbo使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • JDBC如何通过SSL方式连接MySQL

    JDBC如何通过SSL方式连接MySQL

    文章介绍了如何配置MySQL以支持SSL连接,并通过JDBC进行安全连接,主要内容包括查看MySQL SSL支持、创建SSL连接用户、配置用户是否强制使用SSL、JDBC配置导入证书以及使用Go编写一个简单的HTTP文件服务器来提供SSL证书
    2025-02-02
  • SpringBoot用多线程批量导入数据库实现方法

    SpringBoot用多线程批量导入数据库实现方法

    这篇文章主要介绍了SpringBoot用多线程批量导入数据库实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • Java在web页面上的编码解码处理及中文URL乱码解决

    Java在web页面上的编码解码处理及中文URL乱码解决

    这篇文章主要介绍了Java在web页面上的编码解码处理及中文URL乱码解决,文中所介绍的两种使用过滤器解决中文链接乱码的方法非常有效,需要的朋友可以参考下
    2016-02-02
  • 详解通过JDBC进行简单的增删改查(以MySQL为例)

    详解通过JDBC进行简单的增删改查(以MySQL为例)

    JDBC是用于执行SQL语句的一类Java API,通过JDBC使得我们可以直接使用Java编程来对关系数据库进行操作。通过封装,可以使开发人员使用纯Java API完成SQL的执行。
    2017-01-01

最新评论