装饰器模式详解

news2025/4/3 6:30:45

以下是一个结合装饰器模式解决实际开发问题的Java实现案例,涵盖动态扩展功能、多层装饰顺序控制、性能优化等场景需求,附带逐行中文注释:

场景描述

开发一个数据加密传输系统,需满足:
基础功能:原始数据传输
加密扩展:支持AES、Base64、GZIP等加密/压缩组合
动态配置:运行时自由组合加密流程(如AES→GZIP→Base64)
性能监控:统计加密过程耗时
顺序控制:防止重复包装(如多次AES加密)

完整代码实现

import java.util.*;

/**
 * 数据处理器抽象接口(Component)
 */
interface DataProcessor {
    // 处理数据
    byte[] process(byte[] data) throws Exception;
    
    // 获取当前处理类型(用于顺序校验)
    String getProcessorType();
}

/**
 * 基础实现:原始数据传输(ConcreteComponent)
 */
class RawDataProcessor implements DataProcessor {
    @Override
    public byte[] process(byte[] data) {
        System.out.println("传输原始数据,大小:" + data.length + "字节");
        return data;
    }

    @Override
    public String getProcessorType() {
        return "RAW";
    }
}

/**
 * 加密装饰器抽象类(Decorator)
 */
abstract class EncryptionDecorator implements DataProcessor {
    protected final DataProcessor wrapped;
    private final String type;
	
	//这个type不推荐自己装饰使用,耦合度以及复杂度太大,需要开发人员自己保证装饰器不重复,重复了也正常执行。
	//并且如果A装饰B,B装饰C就不能约束住代码了。
    protected EncryptionDecorator(DataProcessor wrapped, String type) { 
            this.wrapped = validateWrapped(wrapped, type);
        this.type = type;
    }

    // 装饰顺序校验:防止重复包装同类处理器
    private DataProcessor validateWrapped(DataProcessor processor, String newType) {
        if (processor.getProcessorType().equals(newType)) {
            throw new IllegalArgumentException("禁止重复添加" + newType + "处理器");
        }
        return processor;
    }

    @Override
    public String getProcessorType() {
        return type;
    }
}

// ------------------- 具体装饰器实现 -------------------

/**
 * AES加密装饰器(ConcreteDecorator)
 */
class AESEncryptor extends EncryptionDecorator {
    public AESEncryptor(DataProcessor processor) {
        super(processor, "AES");
    }

    @Override
    public byte[] process(byte[] data) throws Exception {
        System.out.println("【AES加密】输入数据大小:" + data.length + "字节");
        byte[] processed = wrapped.process(data);
        // 模拟AES加密(实际应使用加密库)
        byte[] encrypted = Arrays.copyOf(processed, processed.length + 16); // +16模拟加密后长度
        System.out.println("【AES加密】输出数据大小:" + encrypted.length + "字节");
        return encrypted;
    }
}

/**
 * Base64编码装饰器(ConcreteDecorator)
 */
class Base64Encoder extends EncryptionDecorator {
    public Base64Encoder(DataProcessor processor) {
        super(processor, "BASE64");
    }

    @Override
    public byte[] process(byte[] data) throws Exception {
        System.out.println("【Base64编码】输入数据大小:" + data.length + "字节");
        byte[] processed = wrapped.process(data);
        // 模拟Base64编码(实际应使用Base64库)
        String encoded = new String(processed) + "_base64";
        System.out.println("【Base64编码】输出数据大小:" + encoded.length() + "字节");
        return encoded.getBytes();
    }
}

/**
 * GZIP压缩装饰器(ConcreteDecorator)
 */
class GZIPCompressor extends EncryptionDecorator {
    public GZIPCompressor(DataProcessor processor) {
        super(processor, "GZIP");
    }

    @Override
    public byte[] process(byte[] data) throws Exception {
        System.out.println("【GZIP压缩】输入数据大小:" + data.length + "字节");
        byte[] processed = wrapped.process(data);
        // 模拟压缩(实际应使用GZIP库)
        byte[] compressed = Arrays.copyOfRange(processed, 0, processed.length / 2);
        System.out.println("【GZIP压缩】输出数据大小:" + compressed.length + "字节");
        return compressed;
    }
}

/**
 * 监控装饰器(扩展功能)
 */
class MonitoringDecorator implements DataProcessor {
    private final DataProcessor wrapped;
    private long processTime;

    public MonitoringDecorator(DataProcessor processor) {
        this.wrapped = processor;
    }

    @Override
    public byte[] process(byte[] data) throws Exception {
        long start = System.nanoTime();
        byte[] result = wrapped.process(data);
        processTime = System.nanoTime() - start;
        return result;
    }

    @Override
    public String getProcessorType() {
        return "MONITOR";
    }

    public long getProcessTime() {
        return processTime;
    }
}

// ------------------- 装饰器工厂(解决顺序问题) -------------------
// 建造者模式,这里其实也可以拓展一个抽象工厂,不过会使复杂度几何增加
//如果当前以及未来设计装饰产品族不够多的情况下建议不适用工厂
class ProcessorBuilder {
    private DataProcessor processor;
    private final Set<String> addedTypes = new HashSet<>();

    public ProcessorBuilder(DataProcessor baseProcessor) {
        this.processor = baseProcessor;
        addedTypes.add(baseProcessor.getProcessorType());
    }

    public ProcessorBuilder addAES() {
        return addDecorator(new AESEncryptor(processor), "AES");
    }

    public ProcessorBuilder addBase64() {
        return addDecorator(new Base64Encoder(processor), "BASE64");
    }

    public ProcessorBuilder addGZIP() {
        return addDecorator(new GZIPCompressor(processor), "GZIP");
    }

    public ProcessorBuilder addMonitoring() {
        return addDecorator(new MonitoringDecorator(processor), "MONITOR");
    }

    private ProcessorBuilder addDecorator(DataProcessor decorator, String type) {
        if (!addedTypes.add(type)) {
            throw new IllegalStateException(type + "处理器已存在");
        }
        this.processor = decorator;
        return this;
    }

    public DataProcessor build() {
        return processor;
    }
}

// ------------------- 客户端代码 -------------------
public class DecoratorPatternDemo {
    public static void main(String[] args) throws Exception {
        // 构建处理链:原始数据 → AES → GZIP → Base64 → 监控
        DataProcessor processor = new ProcessorBuilder(new RawDataProcessor())
                .addAES()
                .addGZIP()
                .addBase64()
                .addMonitoring()
                .build();

        // 测试数据处理
        byte[] originalData = "Hello Decorator Pattern!".getBytes();
        byte[] result = processor.process(originalData);

        // 获取监控数据,这里能成功的原因是因为最后加入Monitoring,数据类型变成这种
        if (processor instanceof MonitoringDecorator) {
            long nanos = ((MonitoringDecorator) processor).getProcessTime();
            System.out.printf("\n处理耗时:%.2f ms", nanos / 1_000_000.0);
        }

        // 测试异常情况:重复添加处理器
        try {
            new ProcessorBuilder(new RawDataProcessor())
                    .addAES()
                    .addAES(); // 触发重复添加异常
        } catch (IllegalStateException e) {
            System.out.println("\n异常捕获:" + e.getMessage());
        }
    }
}

真实场景问题解决

问题类型解决方案
功能组合爆炸 -动态组合装饰器,避免继承导致的类膨胀
装饰顺序敏感 -通过Builder控制添加顺序,构造函数校验重复类型
性能监控困难 -添加非侵入式监控装饰器
代码侵入性高 -核心类保持纯净,扩展功能通过装饰器实现
维护成本高 -每个装饰器独立维护,符合单一职责原则
多层嵌套调试困难-为每个装饰器添加日志输出,清晰展示处理流程

装饰器模式优势分析

动态扩展能力

// 运行时自由组合加密流程
DataProcessor p1 = new AESEncryptor(new GZIPCompressor(new RawDataProcessor()));
DataProcessor p2 = new Base64Encoder(new AESEncryptor(new RawDataProcessor()));

符合开闭原则

新增功能:只需添加新的装饰器类
原有代码:无需修改核心组件和已有装饰器
替代多重继承

传统继承方式:
AES_GZIP_Processor
AES_Base64_Processor
GZIP_Base64_Processor
...

装饰器模式:
AESEncryptor → GZIPCompressor → Base64Encoder
职责清晰分离
每个装饰器只关注单一功能
业务逻辑与基础设施分离(如加密算法与性能监控)
性能优化技巧
缓存装饰器实例

class DecoratorCache {
    private static Map<String, DataProcessor> cache = new ConcurrentHashMap<>();
    
    public static DataProcessor getDecorator(DataProcessor base, String type) {
        String key = base.hashCode() + "_" + type;
        return cache.computeIfAbsent(key, k -> createDecorator(base, type));
    }
}

异步处理装饰器

class AsyncDecorator implements DataProcessor {
    private final Executor executor = Executors.newCachedThreadPool();
    
    public byte[] process(byte[] data) {
        executor.execute(() -> wrapped.process(data));
        return data; // 立即返回,异步处理
    }
}

批量处理优化

class BatchDecorator implements DataProcessor {
    public byte[] process(byte[] data) {
        // 将大数据拆分为块处理
        byte[][] chunks = splitData(data);
        Arrays.parallelSetAll(chunks, i -> wrapped.process(chunks[i]));
        return combineChunks(chunks);
    }
}

模式对比

模式核心思想装饰器模式特点
适配器模式接口转换不改变接口,只增强功能
代理模式控制访问透明增强,客户端无感知
策略模式算法替换动态叠加算法而非替换
组合模式树形结构装饰器链是线性结构

行业应用案例

Java IO流体系

InputStream in = new BufferedInputStream(
                 new GZIPInputStream(
                 new FileInputStream("data.gz")));

Web中间件

HttpServletRequest wrappedRequest = new LoggingRequestWrapper(
                                   new CachingRequestWrapper(
                                   new XSSFilterWrapper(rawRequest)));

游戏装备系

Weapon weapon = new LaserSight(
                new ExtendedMagazine(
                new BaseRifle()));

通过这个案例可以看出,装饰器模式在需要动态增强对象功能且要求扩展灵活的场景中表现优异。实际开发中建议:
控制装饰层数:避免过度嵌套影响性能
明确装饰顺序:通过Builder等模式管理
注意对象标识:装饰后会改变对象hashCode/equals行为
结合其他模式:如工厂模式创建装饰器链

一句话总结

装饰器模式是通过组合的方式将一个实现了共同接口或抽象类的对象进行包装,并在执行时按照装饰的顺序进行递归调用,以实现功能的动态扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2326375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IP 地址规划中的子网划分:/18 网络容纳 64 个 C 段(/24)的原理与应用解析

整体表格说明 这是某市教育城域网中某县教育相关机构的IP地址规划表&#xff0c;明确了某县一中和某县教育局的IP地址范围&#xff0c;包括终端使用地址段、业务互访地址段。 概念解析 64个C段终端及互联地址 C段地址&#xff1a;一个C段是IP地址中的一个/24网络&#xff08;…

linux下Tomcat配置提示权限不够解决办法

文章目录 前言解决方案 前言 往linux服务器上部署Java后端&#xff0c;但是在服务器上安装好的tomcat&#xff0c;却因为权限不够无法进入 这就导致后端war包项目及前端页面无法部署 解决方案 sudo chmod -R 777 /opt/tomcat/webapps修改tomcat目录下的权限即可&#xff0c;对…

您使用的开源软件许可证是否存在冲突呢?

开源软件代码使用现状 根据最新发布的《第三次自由和开源软件普查报告》&#xff0c;96%的代码库中使用了开源组件&#xff0c;这表明开源技术在现代软件开发中占据了核心地位。在国内企业软件项目中&#xff0c;开源软件的使用率达到了100%&#xff0c;平均每个项目使用了166…

leetcode刷题日记——接雨水

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; 题目要求求凹进去的部分能接多少雨水&#xff0c;即有多少个格子可以从第一个高度快出发去寻找下一个高于或者等于他的格子&#xff0c;然后计算其中的差值 有高于或等于他的格子&#xff0c;计算他俩中间能装的雨水当后续没有…

阿里巴巴暑期实习Java面经,灵犀互娱一面

哈希表熟悉吗&#xff0c;可以如何实现&#xff1f; 开散列版本什么时候需要扩容 高并发服务器内的主从reactor模型是如何实现的&#xff1f; 进程 线程 协程 的区别&#xff1f; 如何保证线程安全 &#xff1f; 了解读写锁吗&#xff1f; 单例模式有了解吗&#xff1f; 可以怎…

AI知识补全(十四):零样本学习与少样本学习是什么?

名人说&#xff1a;一笑出门去&#xff0c;千里落花风。——辛弃疾《水调歌头我饮不须劝》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知识补全&#xff08;十三&#xff09;&#xff1a;注意力…

如何用Postman实现自动化测试?

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 这里简单演示在postman中怎样实现自动化测试&#xff08;不涉及到用户登录的token认证&#xff09; 导入测试用例文件&#xff0c;测试web接口 postman使用流程…

LeetCode Hot100 刷题笔记(9)—— 二分查找、技巧

目录 前言 一、二分查找 1. 搜索插入位置 2. 搜索二维矩阵 3. 在排序数组中查找元素的第一个和最后一个位置 4. 搜索旋转排序数组 5. 寻找旋转排序数组中的最小值 6. 寻找两个正序数组的中位数 二、技巧 1. 只出现一次的数字 2. 多数元素 3. 颜色分类 4. 下一个排列 5. 寻找重复…

Ubuntu 系统上完全卸载 Docker

以下是在 Ubuntu 系统上完全卸载 Docker 的分步指南 一.卸载验证 二.卸载步骤 1.停止 Docker 服务 sudo systemctl stop docker.socket sudo systemctl stop docker.service2.卸载 Docker 软件包 # 移除 Docker 核心组件 sudo apt-get purge -y \docker-ce \docker-ce-cli …

Python----计算机视觉处理(Opencv:道路检测之道路透视变换)

一、透视变换 对于道路检测来说&#xff0c;为了方便车辆进行行驶&#xff0c;道路上都有车道线&#xff0c;为了更加方便对道路线进行检测&#xff0c;首先我们要把到路线平视图转变为俯视图&#xff0c;以便后期处理更加方便&#xff0c;如下图所示&#xff0c;该为虚拟场景的…

为什么 ThreadLocalMap 的 key 是弱引用 value是强引用

问题一&#xff1a;为什么 ThreadLocalMap 的 key 是弱引用&#xff1f; 【假设 Entry 的 key 是对 ThreadLocal 对象的强引用】&#xff1a;这个 Entry 又持有 ThreadLocal 对象和 value 对象的强引用。如果在其他地方都没有对这个 ThreadLocla 对象的引用了、然后在使用 Thr…

AI 能解开内容的「不可能三角」吗?

3月21日&#xff0c;以“‘AI商业’进化论”为主题的行业峰会在中欧国际工商学院上海校区成功举行&#xff0c;并发布人工智能与商业创新白皮书。本次活动由中欧国际工商学院与特赞科技Tezign联合主办&#xff0c;中欧特赞人工智能与商业创新研究基金承办&#xff0c;中欧AI与营…

计算机网络 OSI参考模型

目录 OSS七层 OSI通信过程1 OSI通信过程2 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 OSS七层 OSI通信过程1 OSI通信过程2 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层

探索新一代大模型代理(LLM agent)及其架构

在人工智能大模型(AI)的浪潮中&#xff0c;2023年我们见证了检索增强生成(Retrieval Augmented Generation, RAG)的兴起&#xff0c;而2024年则无疑成为了“代理”agent的元年。各大AI企业纷纷投身于聊天机器人代理的研发中&#xff0c;工具如MultiOn通过与外部网站的连接实现了…

AI应用案例(1)——智能工牌和会话质检

今天开辟一个新的模块&#xff0c;自己平时也搜集一些典型的行业应用案例&#xff0c;不如就记录到C站&#xff0c;同时和大家也是个分享好了。 今天分享的企业和产品&#xff0c;是循环智能的智能工牌。 这个产品应用场景清晰&#xff0c;针对的行业痛点合理&#xff0c;解决…

HMTL+JS+CSS实现贪吃蛇游戏,包含有一般模式,困难模式,还有无敌模式

HMTLJSCSS实现贪吃蛇游戏&#xff0c;包含有一般模式&#xff0c;困难模式&#xff0c;还有无敌模式&#xff08;可以穿墙死不了&#xff0c;从左边进去可以从右边出来&#xff09;&#xff0c;显示当前分数和最高分&#xff0c;吃到的球颜色可以叠加到蛇身体上 为了适配手机端…

内网渗透——红日靶场二

目录 一、前期准备 DC机配置 PC机配置 WEB机配置 将PC机和WEB机的IP地址进行更改 开启WEB服务 二、外网探测 1.使用nmap扫描 2.目录扫描 3.漏洞扫描 &#xff08;1&#xff09;CVE-2017-3506&#xff08;getshell失败&#xff09; &#xff08;2&#xff09;CVE-201…

【Unity】处理文字显示不全的问题

1.选中字体文件&#xff0c;检查 MultiAtlasTeextures 是否勾选&#xff0c;未勾选的话&#xff0c;先勾选保存后查看是否显示正常 2.勾选后未正常显示&#xff0c;则在搜索框中输入未显示的文本&#xff0c;确认字体图集是否包含该文本&#xff0c;然后点击Update Atlas Textu…

汽车诊断开发入门以及OBD检测

一、OBD 概述 定义&#xff1a;OBD 即 On - Board Diagnostics&#xff0c;车载自动诊断系统。它能实时监测车辆各项系统和部件状态&#xff0c;以此帮助诊断故障并预警。设计初衷与发展&#xff1a;最初设计目的是控制汽车尾气排放&#xff0c;确保符合环境标准。随着技术进步…

Ingredient-oriented Multi-Degradation Learning for Image Restoration论文阅读

摘要&#xff1a;重点在于关联多个任务本质的联系。 不同恢复任务的关联性很重要。 揭示退化现象的内在机理联系很有意义。 多合一的方法能在单一模型中处理多种退化问题&#xff0c;可扩展性较差。 成分导向范式挖掘不同图像退化现象背后的物理规律或特征模式。 成分导向退化重…