【设计模式】结构型模式(四):组合模式、享元模式
《设计模式之结构型模式》系列,共包含以下文章:
- 结构型模式(一):适配器模式、装饰器模式
- 结构型模式(二):代理模式
- 结构型模式(三):桥接模式、外观模式
- 结构型模式(四):组合模式、享元模式
😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!
结构型模式(四):组合模式、享元模式
- 6.组合模式(Composite)
- 6.1 案例
- 6.1.1 定义统一接口
- 6.1.2 实现叶子节点(文件)
- 6.1.3 实现组合节点(文件夹)
- 6.1.4 客户端
- 6.1.5 输出
- 7.享元模式(Flyweight)
- 7.1 问题
- 7.2 解决方案
- 7.3 代码实现
- 7.3.1 享元接口
- 7.3.2 享元对象
- 7.3.3 享元工厂
- 7.3.4 客户端
- 7.3.5 输出结果
6.组合模式(Composite)
组合模式(Composite Pattern)是一种设计模式,用于处理树形结构的数据。它的主要目的是将对象组合成树形结构来表示 “部分 - 整体” 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

具体来说,组合模式有以下几个特点:
- 统一接口:无论是单个对象(叶子节点)还是组合对象(树枝节点),都提供相同的接口,这样客户端代码可以一致地处理它们,而不需要关心它们是单个对象还是组合对象。
- 递归结构:组合模式通过 递归 的方式构建树形结构,每个组合对象可以包含多个子对象,这些子对象可以是叶子节点或更深层次的组合对象。
- 透明性:客户端代码不需要关心对象的具体类型,只需要通过统一的接口进行操作。
6.1 案例
假设你正在开发一个文件系统,文件系统中包含文件(File)和文件夹(Folder)。文件夹可以包含多个文件和其他文件夹,文件夹和文件都有一些共同的操作,比如显示内容。

6.1.1 定义统一接口
public interface Component {void display();
}
6.1.2 实现叶子节点(文件)
public class File implements Component {private String name;public File(String name) {this.name = name;}@Overridepublic void display() {System.out.println("文件: " + name);}
}
6.1.3 实现组合节点(文件夹)
import java.util.ArrayList;
import java.util.List;public class Folder implements Component {private String name;private List<Component> children = new ArrayList<>();public Folder(String name) {this.name = name;}public void add(Component component) {children.add(component);}public void remove(Component component) {children.remove(component);}@Overridepublic void display() {System.out.println("文件夹: " + name);for (Component child : children) {child.display();}}
}
6.1.4 客户端
public class CompositePatternExample {public static void main(String[] args) {// 创建文件和文件夹File file1 = new File("文件1");File file2 = new File("文件2");Folder folder1 = new Folder("文件夹1");Folder folder2 = new Folder("文件夹2");// 组合文件和文件夹folder1.add(file1);folder1.add(file2);folder2.add(folder1);folder2.add(new File("文件3"));// 显示文件系统结构folder2.display();}
}

6.1.5 输出
文件夹: 文件夹2
文件夹: 文件夹1
文件: 文件1
文件: 文件2
文件: 文件3
在这个例子中,Component 接口定义了所有组件的共同操作,File 和 Folder 都实现了这个接口。Folder 可以包含多个 Component,从而形成树形结构。客户端代码通过 Component 接口操作文件和文件夹,而不需要关心它们的具体类型。
7.享元模式(Flyweight)
享元模式(Flyweight Pattern) 是一种用于性能优化的设计模式,主要目的是通过共享已经存在的对象来减少内存使用和提高性能。简单来说,享元模式 通过共享对象来减少对象的数量,从而节省内存。

假设你正在开发一个文本编辑器,用户可以在编辑器中输入大量的文本。为了显示这些文本,你需要为每个字符创建一个对象。如果每个字符都创建一个独立的对象,那么当文本非常大时,会占用大量的内存。
7.1 问题
- 内存消耗大:如果每个字符都创建一个独立的对象,内存消耗会非常大。
- 性能问题:创建和管理大量对象会导致性能下降。
7.2 解决方案
使用享元模式,你可以共享字符对象,而不是为每个字符都创建一个独立的对象。具体来说:
- 内部状态(
Intrinsic State):这些是共享的、不变的状态,例如字符的字形信息。 - 外部状态(
Extrinsic State):这些是不共享的、变化的状态,例如字符在文档中的位置。
🚀 计算机世界中无穷无尽的可能,其本质都是由
1和0两个 “元” 的组合变化而产生的。
🚀 元,顾名思义,始也,有本初、根源的意思。“享元” 则是 共享元件 的意思。享元模式的英文Flyweight是 轻量级 的意思,这就意味着享元模式能使程序变得更加轻量化。当系统存在大量的对象,并且这些对象又具有相同的内部状态时,我们就可以用享元模式共享相同的元件对象,以避免对象泛滥造成资源浪费。
7.3 代码实现
假设你有一个文本编辑器,需要显示大量的字符。你可以使用享元模式来减少内存使用。
7.3.1 享元接口
public interface CharacterFlyweight {void display(int position);
}
- 定义了一个接口,用于显示字符及其位置。
- 方法:
display(int position),用于显示字符在文本中的位置。
7.3.2 享元对象
public class CharacterObject implements CharacterFlyweight {private final char value;private final String font;public CharacterObject(char value, String font) {this.value = value;this.font = font;}@Overridepublic void display(int position) {System.out.println("Character: " + value + " at position: " + position + " with font: " + font);}
}
Character 类实现了 CharacterFlyweight 接口,包含字符的值和字体信息。这些信息是内部状态,是共享的。
7.3.3 享元工厂
import java.util.HashMap;
import java.util.Map;public class CharacterFactory {private static final Map<Character, CharacterFlyweight> pool = new HashMap<>();public static CharacterFlyweight getCharacter(char value, String font) {CharacterFlyweight character = pool.get(value);if (character == null) {// 创建新的 CharacterFlyweight 对象System.out.println("===============: " + value + " 加入共享");character = new CharacterObject(value, font);// 将新创建的对象添加到 pool 中pool.put(value, character);}return character;}
}
CharacterFactory 类是一个工厂类,用于管理共享的字符对象。通过 getCharacter 方法,根据字符值和字体信息从池中获取或创建字符对象。
为什么 CharacterFactory 不需要实现 CharacterFlyweight 接口?
- 职责分离:
CharacterFactory的职责是创建和管理CharacterFlyweight对象,而不是实现CharacterFlyweight接口。实现接口的类应该是具体的字符对象类,如CharacterObject。 - 灵活性:通过工厂类创建对象,可以在不改变调用代码的情况下,轻松地更换不同的实现类。例如,如果将来需要添加一个新的字符对象实现类
CharacterObject2,只需要在CharacterFactory中创建CharacterObject2的实例即可。 - 解耦:调用者只需要知道
CharacterFlyweight接口,而不需要知道具体的实现类。这有助于降低代码的耦合度,提高代码的可维护性和扩展性。
在 CharacterFactory 类中,getCharacter 方法负责创建 CharacterFlyweight 对象。具体来说,当 pool 中没有指定字符的 CharacterFlyweight 对象时,getCharacter 方法会创建一个新的 CharacterObject 实例,并将其添加到 pool 中。
7.3.4 客户端
public class FlyweightPatternExample {public static void main(String[] args) {// 模拟输入文本String text = "Hello, World!";for (int i = 0; i < text.length(); i++) {char c = text.charAt(i);CharacterFlyweight character = CharacterFactory.getCharacter(c, "Arial");character.display(i);}}
}
客户端代码通过 CharacterFactory 获取字符对象,并调用 display 方法显示字符。每个字符对象在池中只创建一次,多次使用时直接从池中获取,从而减少了内存使用。
7.3.5 输出结果
===============: H 加入共享
Character: H at position: 0 with font: Arial
===============: e 加入共享
Character: e at position: 1 with font: Arial
===============: l 加入共享
Character: l at position: 2 with font: Arial
Character: l at position: 3 with font: Arial
===============: o 加入共享
Character: o at position: 4 with font: Arial
===============: , 加入共享
Character: , at position: 5 with font: Arial
===============: 加入共享
Character: at position: 6 with font: Arial
===============: W 加入共享
Character: W at position: 7 with font: Arial
Character: o at position: 8 with font: Arial
===============: r 加入共享
Character: r at position: 9 with font: Arial
Character: l at position: 10 with font: Arial
===============: d 加入共享
Character: d at position: 11 with font: Arial
===============: ! 加入共享
Character: ! at position: 12 with font: Arial

相关文章:
【设计模式】结构型模式(四):组合模式、享元模式
《设计模式之结构型模式》系列,共包含以下文章: 结构型模式(一):适配器模式、装饰器模式结构型模式(二):代理模式结构型模式(三):桥接模式、外观…...
分布式数据库中间件mycat
MyCat MyCat是一个开源的分布式数据库系统,它实现了MySQL协议,可以作为数据库代理使用。 MyCat(中间件)的核心功能是分库分表,即将一个大表水平分割为多个小表,存储在后端的MySQL服务器或其他数据库中。 它不仅支持MySQLÿ…...
放大电路中的反馈 > 负反馈 > 四种组态 > 虚断和虚短
零、什么是反馈?为什么反馈很重要?而且负反馈最重要? 反馈在所有领域都是很美的东西: 公司出台某项政策,过了一个月让大家谈谈新政策的感受,然后公司对政策进行适当调整。 高三月考可以反应你对各个学课的…...
STM32F405RGT6单片机原理图、PCB免费分享
大学时机创比赛时画的板子,比到一半因为疫情回家,无后续,,,已打板验证过,使用stm32f405rgt6做主控 下载文件资源如下 原理图文件 pcb文件 外壳模型文件 stm32f405例程 功能 以下功能全部验证通过 4路…...
大语言模型鼻祖Transformer的模型架构和底层原理
Transformer 模型的出现标志着自然语言处理(NLP)技术的一次重大进步。这个概念最初是针对机器翻译等任务而提出的,Transformer 后来被拓展成各种形式——每种形式都针对特定的应用,包括原始的编码器-解码器(encoder-de…...
GB/T 43206—2023信息安全技术信息系统密码应用测评要求(五)
文章目录 附录AA.1 概述A.2 密钥产生A.3 密钥分发A.4 密钥存储A.5 密钥使用A.6 密钥更新A.7 密钥归档A. 8 密钥撤销A.9 密钥备份A.10 密钥恢复A.11 密钥销毁 附录B附录C 附录A A.1 概述 密钥管理对于保证密钥全生存周期的安全性至关重要 ,可以保证密钥(除公开密钥外) 不被非授…...
深度学习:BERT 详解
BERT 详解 为了全面详细地解析BERT(Bidirectional Encoder Representations from Transformers),我们将深入探讨它的技术架构、预训练任务、微调方法及其在各种自然语言处理(NLP)任务中的应用。 一、BERT的技术架构 …...
智能的编织:C++中auto的编织艺术
在C的世界里,auto这个关键字就像是一个聪明的助手,它能够自动帮你识别变量的类型,让你的代码更加简洁和清晰。下面,我们就来聊聊auto这个关键字的前世今生,以及它在C11标准中的新用法。 auto的前世 在C11之前&#x…...
订单分库分表
一、引言 在当今互联网时代,随着电商、金融等行业的快速发展,订单数量呈爆炸式增长。传统的单一数据库存储订单信息的方式面临着巨大的挑战,如数据存储容量有限、查询性能下降、数据备份和恢复困难等。为了解决这些问题,分库分表技…...
【温度表达转化】
【温度表达转化】 C语言代码C代码Java代码Python代码 💐The Begin💐点点关注,收藏不迷路💐 利用公式 C5∗(F−32)/9 (其中C表示摄氏温度,F表示华氏温度) 进行计算转化。 输出 输出一行&#x…...
封装一个web Worker 处理方法实现多线程
背景: 开启多线程处理一段耗时的逻辑 简化Worker使用 直接上代码: 以下是封装的函数直接复制即可 /*** 封装一个worker的启动函数 用于开启一个新的线程 来处理一些耗时的操作* param {object} paremdata 传递给worker的参数* param {function} call…...
unity3d————屏幕坐标,GUI坐标,世界坐标的基础注意点
在Unity3D中,GUI控件的起始坐标与屏幕坐标的起始点并不完全相同,具体说明如下: GUI控件的起始坐标 绘制GUI界面时使用的坐标以屏幕的左上角为(0,0)点,右下角为(Screen.width, Screen.Height)。不过,对于GUI控件的具体…...
MySQL基础-单表查询
语法 select [distinct] 列名1,列名2 as 别名... from数据表名 where组前筛选 group by分组字段 having组后筛选 order by排序的列 [asc | desc] limit 起始索引,数据条数 测试数据 # 建测试表 create table products (id int primary key a…...
Web安全之SQL注入---基础
文章目录 SQL注入简介SQL注入基础SQL注入分类SQL注入流程 SQL注入简介 什么是SQL注入? SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理…...
MongoDB笔记03-MongoDB索引
文章目录 一、前言1.1 概述1.2 MongoDB索引使用B-Tree还是BTree?1.3 B 树和 B 树的对比1.4 总结 二、索引的类型2.1 单字段索引2.2 复合索引2.3 其他索引 三、索引的管理操作3.1 索引的查看3.2 索引的创建3.2.1 单字段索引3.2.2 复合索引 3.3 索引的移除3.3.1 指定索…...
Docker基础(一)
Docker 简介 常用命令 镜像 #搜索镜像 docker search nginx #下载镜像 docker pull nginx #下载指定版本镜像 docker pull nginx:1.26.0 #查看所有镜像 docker images #删除指定id的镜像 docker rmi e784f4560448 # 删除多个镜像 docker rmi bde7d154a67f 94543a6c1aef e784…...
解决 IntelliJ IDEA Maven 项目 JDK 版本自动变为 1.5 的问题
一、问题描述 在使用 IntelliJ IDEA 创建 Maven 项目时,经常会遇到一个问题:项目的默认编译版本被设置为 JDK 1.5,即使系统中安装的是更高版本的 JDK。这不仅会导致编译时出现警告,还可能引起兼容性问题。每次手动修改编译版本后…...
SDL事件相关
文章目录 事件相关的函数和数据结构用户自定义事件代码相关: 事件相关的函数和数据结构 SDL_WaitEvent :等待一个事件SDL_PushEvent 发送一个事件SDL_PumpEvents(): 将硬件设备产生的时间放入事件队列 ,用于读取事件,在调用该函数之前&#…...
探索App Intents:让你的应用与Siri无缝互动的新方式
苹果推出了一个新框架——App Intents,使开发者可以在iOS 18.2、macOS 15.2等平台上集成Siri和Apple Intelligence,实现对应用内容的读取和操作。 App Intents使应用的功能和内容能无缝融入系统体验中,例如Siri、Spotlight搜索、快捷指令和小…...
冒泡排序法
编写程序实现冒泡排序。 相关知识 为了完成本关任务,要了解冒泡法排序的算法思想: 对所有相邻记录的关键字值进行比较,如果是逆序则将其交换,最终达到有序化,其处理过程为: 将整个待排序的记录序列划分成…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...
echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式
pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图,如果边框加在dom上面,pdf-lib导出svg的时候并不会导出边框,所以只能在echarts图上面加边框 grid的边框是在图里…...
C# WPF 左右布局实现学习笔记(1)
开发流程视频: https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码: GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用(.NET Framework) 2.…...
智能体革命:企业如何构建自主决策的AI代理?
OpenAI智能代理构建实用指南详解 随着大型语言模型(LLM)在推理、多模态理解和工具调用能力上的进步,智能代理(Agents)成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同,智能代理能够自主执行工…...
Redis专题-实战篇一-基于Session和Redis实现登录业务
GitHub项目地址:https://github.com/whltaoin/redisLearningProject_hm-dianping 基于Session实现登录业务功能提交版本码:e34399f 基于Redis实现登录业务提交版本码:60bf740 一、导入黑马点评后端项目 项目架构图 1. 前期阶段2. 后续阶段导…...
