【设计模式】结构型模式(四):组合模式、享元模式
《设计模式之结构型模式》系列,共包含以下文章:
- 结构型模式(一):适配器模式、装饰器模式
- 结构型模式(二):代理模式
- 结构型模式(三):桥接模式、外观模式
- 结构型模式(四):组合模式、享元模式
😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!
结构型模式(四):组合模式、享元模式
- 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搜索、快捷指令和小…...
冒泡排序法
编写程序实现冒泡排序。 相关知识 为了完成本关任务,要了解冒泡法排序的算法思想: 对所有相邻记录的关键字值进行比较,如果是逆序则将其交换,最终达到有序化,其处理过程为: 将整个待排序的记录序列划分成…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
