java设计模式八 享元
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享技术有效地支持大量细粒度的对象。这种模式通过存储对象的外部状态在外部,而将不经常变化的内部状态(称为享元)存储在内部,以此来减少内存中对象的数量。享元模式适用于那些可以共享的对象,以此来减少系统创建对象的数量,从而提升效率和性能。
### 案例分析:文字处理软件中的字符渲染
假设我们正在开发一个文字处理软件,需要显示大量的字符。每个字符可能有不同的颜色、字体和大小等属性,但字符本身(如'a', 'b', 'c'等)却是有限的。如果不使用享元模式,我们会为每个带有独特属性组合的字符创建一个对象,这会导致内存中存在大量重复的字符对象。
#### 享元模式的应用步骤:
1. **定义享元接口**:
定义一个接口或抽象类,声明一个或多个方法用于操作内部状态,通常还会有一个方法来获取外部状态。
```java
public interface Character {void display(char character, Color color, Font font);
}
```
2. **实现享元类**:
实现享元接口,并存储内部状态(在这个案例中,内部状态为空,因为字符是固定的)。
```java
public class ConcreteCharacter implements Character {@Overridepublic void display(char character, Color color, Font font) {System.out.printf("Rendering character '%c' with color %s and font %s%n", character, color, font);}
}
```
3. **创建享元工厂**:
工厂负责创建和管理享元对象,确保相同的字符复用同一个对象。为了高效地查找享元,通常会使用如HashMap这样的数据结构来存储已经创建的享元对象。```java
public class CharacterFactory {private Map<Character, ConcreteCharacter> pool = new HashMap<>();public Character getCharacter(char character) {ConcreteCharacter flyweight = pool.get(character);if (flyweight == null) {flyweight = new ConcreteCharacter();pool.put(character, flyweight);}return flyweight;}
}
```
4. **客户端代码**:
客户端从享元工厂获取享元对象,并设置外部状态(颜色、字体)后调用其方法。
```java
public class TextEditor {private CharacterFactory factory;public TextEditor() {factory = new CharacterFactory();}public void renderText(String text, Color color, Font font) {for (char c : text.toCharArray()) {Character character = factory.getCharacter(c);character.display(c, color, font);}}
}// 使用示例
TextEditor editor = new TextEditor();
editor.renderText("Hello, World!", Color.RED, Font.TIMES_ROMAN);
```
在这个例子中,尽管每个字符在屏幕上显示时可能有不同的颜色和字体,但字符本身是有限的。享元模式通过`CharacterFactory`确保了对于每个不同的字符仅创建一个`ConcreteCharacter`实例,并通过传递外部状态(颜色和字体)来展示字符的不同表现,从而极大地减少了内存占用。
继续深入,我们可以探讨享元模式的一些关键优势、应用场景及注意事项,以加深对这一设计模式的理解。
### 关键优势
1. **内存优化**:最显著的优势是大幅度减少内存中对象的数量,特别是当存在大量相似对象时。这在处理大数据量或图形密集型应用时尤为重要,能有效减少内存占用,提高系统性能。
2. **性能提升**:减少了对象的创建和销毁过程,降低了垃圾回收的压力,提升了系统的运行速度。
3. **易于维护和扩展**:通过集中管理享元对象,使得对内部状态的修改变得更加集中和容易控制。同时,新的享元类型可以轻松添加到系统中,不影响现有代码。
### 应用场景
- **图形处理**:如上述字符渲染例子,图像处理软件中的像素、图标库等。
- **大量相似对象**:游戏开发中的子弹、棋盘上的棋子等,这些对象除了位置等少数状态外,其他大部分属性都相同。
- **字符串处理**:文本编辑器中的字符或单词,特别是在实现复杂格式化时。
- **网络编程**:连接池的设计中,复用数据库连接或socket连接等资源。
### 注意事项
1. **区分内外部状态**:正确区分对象的内部状态(被享元对象管理,不随环境改变)和外部状态(由客户端控制,随环境改变)。错误地处理状态可能会导致逻辑错误或数据混乱。
2. **线程安全**:当享元对象在多线程环境下被共享时,需要考虑线程安全问题。可能需要在享元类中加入同步机制或确保外部状态的线程安全性。
3. **初始化成本**:虽然享元模式能减少运行时的内存消耗,但如果享元对象的创建成本很高,初始化大量享元可能会影响程序的启动时间。
4. **过度设计**:在对象数量不是特别庞大,或者内存不是主要瓶颈的情况下,过度使用享元模式可能会增加系统的复杂度,得不偿失。因此,是否采用享元模式需根据实际情况权衡。
通过合理运用享元模式,可以在保证程序高效、内存友好的同时,保持代码的清晰与可维护性,是面向对象设计中一项非常实用的技巧。
接下来,让我们进一步探索享元模式在实际开发中的一些高级应用技巧和设计考量,以及如何与其他设计模式结合使用,以达到更好的效果。
### 高级应用技巧
1. **使用复合享元**:当享元对象需要组合其他享元对象以形成更复杂的结构时,可以引入复合享元的概念。例如,在游戏开发中,一个复合角色可能包含武器、防具等多个独立的享元对象,通过组合这些享元来构建更丰富的实体。
2. **懒加载**:为了进一步优化内存使用,可以在享元工厂中实现享元对象的懒加载策略。即首次请求某个享元时才创建它,而不是一开始就创建所有可能用到的享元对象。
3. **缓存管理**:对于内存敏感的应用,可以实施更精细的缓存管理策略,比如设置缓存上限,当缓存超出限制时,根据某种策略(如最近最少使用LRU)移除部分享元对象。
### 与其他设计模式结合
1. **与工厂模式结合**:如之前提到的,享元工厂负责创建和管理享元对象,这是工厂模式的一个典型应用。结合工厂模式,可以更加灵活和高效地控制享元对象的生命周期和分配。
2. **与代理模式结合**:当需要为享元对象添加额外的操作或控制访问时,可以通过代理模式包装享元对象。代理可以处理外部状态的传递,以及任何与具体业务逻辑相关的操作,同时保持享元对象本身的纯粹性。
3. **与状态模式结合**:如果享元对象的行为依赖于其状态,且这些状态变化较为复杂,可以引入状态模式来管理这些状态变化。享元对象的内部状态通过状态对象表示,状态对象的变化不会影响享元对象的身份,从而维持享元的共享特性。
### 总结
享元模式是一个强大的设计工具,它通过共享技术减少系统中对象的数量,优化了内存使用并提高了性能。在设计和实现时,需要细心区分内外部状态,考虑并发访问的线程安全问题,以及选择合适的时间和场景应用该模式。通过与其他设计模式的结合使用,可以解决更为复杂的问题,构建出更加高效、灵活的系统。掌握并灵活运用享元模式,是提升软件设计质量的重要一步。
相关文章:
java设计模式八 享元
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享技术有效地支持大量细粒度的对象。这种模式通过存储对象的外部状态在外部,而将不经常变化的内部状态(称为享元)存储在内部,以此来减…...
ELK原理详解
ELK原理详解 一、引言 在当今日益增长的数据量和复杂的系统环境中,日志数据的收集、存储、分析和可视化成为了企业运营和决策不可或缺的一部分。ELK(Elasticsearch、Logstash、Kibana)堆栈凭借其高效的性能、灵活的扩展性和强大的功能&…...
多线程学习Day09
10.Tomcat线程池 LimitLatch 用来限流,可以控制最大连接个数,类似 J.U.C 中的 Semaphore 后面再讲 Acceptor 只负责【接收新的 socket 连接】 Poller 只负责监听 socket channel 是否有【可读的 I/O 事件】 一旦可读,封装一个任务对象&#x…...
第33次CSP认证Q1:词频统计
🍄题目描述 在学习了文本处理后,小 P 对英语书中的 𝑛n 篇文章进行了初步整理。 具体来说,小 P 将所有的英文单词都转化为了整数编号。假设这 𝑛n 篇文章中共出现了 𝑚m 个不同的单词,则把它们…...
pytorch加载模型出现错误
大概的错误长下面这样: 问题出现的原因: 很明显,我就是犯了第一种错误。 网上的修改方法: 我觉得按道理哈,确实,蓝色部分应该是可以把问题解决了的。但是我没有解决,因为我犯了另外一个错…...
如何在Mac上恢复格式化硬盘的数据?
“嗨,我格式化了我的一个Mac硬盘,而没有使用Time Machine备份数据。这个硬盘被未知病毒感染了,所以我把它格式化为出厂设置。但是,我忘了备份我的文件。现在,我想恢复格式化的硬盘驱动器并恢复我的文档,您能…...
华为OD机试 - 手机App防沉迷系统(Java 2024 C卷 100分)
华为OD机试 2024C卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷C卷)》。 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试…...
搜维尔科技:光学动作捕捉系统用于城市公共安全智慧感知实验室
用户名称:西安科技大学 主要产品:Optitrack Priime41 光学动作捕捉系统(8头) 在6米8米的空间内,通过8个Optitrack Priime41光学动作捕捉镜头,对人体动作进行捕捉,得到用户想要的人体三维空间坐…...
保研面试408复习 4——操作系统、计网
文章目录 1、操作系统一、文件系统中文件是如何组织的?二、文件的整体概述三、UNIX外存空闲空间管理 2、计算机网络一、CSMA/CD 协议(数据链路层协议)二、以太网MAC帧MTU 标记文字记忆,加粗文字注意,普通文字理解。 1、…...
实战攻防中关于文档的妙用
一、PPT钓鱼 简单制作一个用于钓鱼的PPTX文件 一般那种小白不知道PPT也能拿来钓鱼,这里主要是借用PPT中的”动作按钮”, 我们在插入的地方,选择“动作按钮” 然后在弹出的窗口处: 比如填入上线CS的语句:powershell.exe -nop -w …...
【使用ChatGPT的API之前】OpenAI API提供的可用模型
文章目录 一. ChatGPT基本概念二. OpenAI API提供的可用模型1. InstructGPT2. ChatGPT3. GPT-4 三. 在OpenAI Playground中使用GPT模型-ing 在使用GPT-4和ChatGPT的API集成到Python应用程序之前,我们先了解ChatGPT的基本概念,与OpenAI API提供的可用模型…...
【C语言】模拟实现深入了解:字符串函数
🔥引言 本篇将模拟实现字符串函数,通过底层了解更多相关细节 🌈个人主页:是店小二呀 🌈C语言笔记专栏:C语言笔记 🌈C笔记专栏: C笔记 🌈喜欢的诗句:无人扶我青云志 我自…...
钩子函数onMounted定义了太多访问MySQL的操作 导致数据库异常
先放几种后端遇到的异常,多数和数据库有关 pymysql.err.InternalError: Packet sequence number wrong - got 102 expected 1 127.0.0.1 - - [09/May/2024 17:49:37] "GET /monitorLastTenList HTTP/1.1" 500 AttributeError: NoneType object has no at…...
Excel文件解析---超大Excel文件读写
1.使用POI写入 当我们想在Excel文件中写入100w条数据时,使用XSSFWorkbook进行写入时会发现,只有将100w条数据全部加载到内存后才会用write()方法统一写入,效率很低,所以我们引入了SXXFWorkbook进行超大Excel文件读写。 通过设置 …...
TypeScript基础:类型系统介绍
TypeScript基础:类型系统介绍 引言 TypeScript,作为JavaScript的一个超集,引入了类型系统,这为开发大型应用程序带来了诸多好处。本文将介绍TypeScript类型系统的基础知识,帮助初学者理解其概念和用法。 基础知识 …...
【Unity】Unity项目转抖音小游戏(一) 项目转换
UnityWEBGL转抖音小游戏流程 业务需求,开始接触一下抖音小游戏相关的内容,开发过程中记录一下流程。 相关参考: 抖音文档:https://developer.open-douyin.com/docs/resource/zh-CN/mini-game/develop/guide/game-engine/rd-to-SC…...
element-ui 中修改loading加载样式
element-ui 中的 loading 加载功能,默认是全屏加载效果 设置局部,需要自定义样式或者修改样式,方法如下: import { Loading } from element-uiVue.prototype.$baseLoading (text) > {let loadingloading Loading.service({…...
QT登录界面,(页面的切换)
以登陆界面为例,(QDialog) 1.主界面先构造login 的对话框类 int main(int argc, char *argv[]) {QApplication a(argc, argv);//先显示Login的界面Study_Login_Dialog login;............ }2.Login的类,可以用自定义的信号&#…...
计算机毕业设计 | vue+springboot汽车销售管理系统(附源码)
1,项目介绍 本项目基于spring boot以及Vue开发,前端实现基于PanJiaChen所提供的开源后台项目vue-element-admin改造。 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等功能,提供经理和销售两种角色进行管理。 2&…...
一款开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验
Snap.Hutao 胡桃工具箱是一款以 MIT 协议开源的原神工具箱,专为现代化 Windows 平台设计,旨在改善桌面端玩家的游戏体验。通过将既有的官方资源与开发团队设计的全新功能相结合,提供了一套完整且实用的工具集,且无需依赖任何移动设…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
