当前位置: 首页 > news >正文

【设计模式】结构型设计模式之 享元模式

文章目录

  • 介绍
      • 关键概念
  • 应用举例
      • 象棋游戏共享棋子对象
      • 文本编辑器中文字格式设计成享元模式
  • 享元模式在 Java 中的应用
      • 享元模式在包装类缓存中的应用
      • 享元模式在 String 中的应用
  • 对比
      • 享元模式和单例模式的区别
      • 享元模式与缓存的区别
  • 总结
      • 优点
      • 缺点

介绍

享元模式,”享元“ 就是被共享的单元。享元模式的意图就是复用对象节省内存,应用的前提是被共享的对象是不可变的对象。
将对象设计成享元,保留一份实例供多处代码引用这样能减少内存中对象的数量,不允许修改是因为避免一出修改影响其他使用他的代码。

关键概念

  1. 享元(Flyweight):这是模式中的核心对象,可以被多个客户端共享。享元对象需要保持内部状态(Internal State)的共享,而外部状态(External State)则由客户端在使用时传入。
  2. 内部状态(Internal State):存储在享元对象内部,可以被共享,不随环境改变而改变的状态。
  3. 外部状态(External State):随环境改变而改变、不能共享的状态,由客户端传入享元对象,以便在运行时根据外部状态来区分不同的享元实例。
  4. 工厂(Factory):负责创建和管理享元对象,确保有效地复用享元对象,通常会使用缓存来存储已经创建的享元对象,以避免重复创建。

应用举例

象棋游戏共享棋子对象

问题:如果每个棋子都包含 id、文本、颜色、横坐标、纵坐标属性。并且每个游戏房间都有一个棋盘,那么如果游戏大厅中有成千上万个棋盘和对应的棋子。那么将创建大量的棋子对象。
方案:利用享元模式,象棋的棋子的一些属性,例如 id、颜色、文本都是固定不变的,下棋时每个棋盘的棋子也是一样的。所以只需要让棋子对象中保存 id、颜色、文本后被所有棋盘共享即可。每个房间只需要保存棋子的 id 和位置。这样就能节省大量的内存。

文本编辑器中文字格式设计成享元模式

一个文本编辑器,每个输入的字符都可以单独调整文本样式,如果给每个字符都保存一个字符样式对象那么十分浪费内存。并且文本编辑器中,一篇文本往往只有少量的几个文本格式所以设计成享元模式能节省大量内存。

/*** 文本样式类** @author Jean* @date 2024/06/04*/
@Getter
public class CharsetStyle {private int font;private int fontSize;private int colorRgb;public CharsetStyle(int font, int fontSize, int colorRgb) {this.font = font;this.fontSize = fontSize;this.colorRgb = colorRgb;}@Overridepublic boolean equals(Object obj) {if (!(obj instanceof CharsetStyle)) {return false;}CharsetStyle other = (CharsetStyle) obj;return other.font == this.font && this.fontSize == other.fontSize && this.colorRgb == other.colorRgb;}/*** 重写了equals一般要重写hashcode方法* 1. 如果两个对象根据 equals 方法判断是相等的,那么它们的 hashCode 方法必须返回相同的值。* 2. 如果两个对象根据 equals 方法判断是不相等的,那么它们的 hashCode 方法不必返回不同的值,但建议这样做以提高散列表(如 HashMap)的性能。* 违反这些规则可能会导致你的类在集合(尤其是基于散列的集合,如 HashSet 和 HashMap)中的行为不符合预期,比如无法正确识别已存在的元素或者影响集合的性能。因此,重写 equals 时配套重写 hashCode 是一种最佳实践。** @return int*/@Overridepublic int hashCode() {// 使用常数乘法、位移等操作来组合字段,以生成独特的哈希值int result = 17;result = 31 * result + font;result = 31 * result + fontSize;result = 31 * result + colorRgb;return result;}
}
/*** 文本样式工厂** @author Jean* @date 2024/06/04*/
public class CharacterStyleFactory {/*** 用来共享的文本样式*/private static final Set<CharsetStyle> styles = ConcurrentHashMap.newKeySet();/*** 获取样式的工厂** @param font* @param fontSize* @param colorRgb* @return {@link CharsetStyle}*/public static CharsetStyle getCharsetStype(int font, int fontSize, int colorRgb) {CharsetStyle charsetStyle = new CharsetStyle(font, fontSize, colorRgb);for (CharsetStyle style : styles) {if (style.equals(charsetStyle)) {return style;}}styles.add(charsetStyle);return charsetStyle;}}

享元模式在 Java 中的应用

享元模式在包装类缓存中的应用

Integer i1=56;
Integer i2=56;
Integer i3=129;
Integer i4=129;
System.out.pringln(i1==i2); //输出true
System.out.pringln(i3==i4); //输出false

例如上面的代码,会先输出 true 再输出 false

  1. i1 和 i2 自动装箱实际上调用的是 Integer.valueOf()方法,对应的自动拆箱的时候实际上调用的 Integer.intValue()方法。
  2. 在 Integer.valueOf 方法中就用到了享元模式,这个方法中实际上是从一个 Integer 的内部类 IntegerCache.cache 中获取缓存好的 Integer 对象;
    1. IntegerCache 实际上就是 Integer 的一个工厂类虽然没有以 Factory 结尾。
    2. IntegerCache 中缓存了 -128~127 的 Integer 对象,如果调用 Integer.value 的 int 值在其范围内,则会直接从 Cache 中返回。
    3. Integer 对象和其他基本类型的包装对象都是不可变的对象。

为什么默认是 -128~127 这个范围可调吗?

  1. 因为预先创建过多的对象,会占用内存并且导致加载时间延长所以只能在一定范围内缓存 所以缓存了 1 个字节大小的整形值。
  2. 可以调 -Djava.lang.Integer.Cache.high=255 或者 -XX:AutoBoxCacheMax=255

Long、Short、Byte 等基础类型的包装类型都有缓存对应范围的对象

享元模式在 String 中的应用

在 Java 中 有一个字符串常量池,在加载时一些字符串就会被创建到常量池中。后续引用到相同的字符串则可以从常量池中直接获取。这也是享元模式的一种应用。

对比

享元模式和单例模式的区别

  1. 单例模式中一个类只能创建一个对象,享元模式中是创建多个对象后被多处代码共享。
  2. 享元模式有点像单例模式的变体,多例模式,但是仍然有很大的区别。区别在和多例模式的设计意图上
  3. 多例模式单例模式都是意在控制对象的数量,而享元模式的意图是对象共享

享元模式与缓存的区别

  1. 享元模式通过工厂来“缓存”创建好的对象,但是这里的缓存更多的意思是存储。
  2. 缓存系统是为了提高访问效率而存在的,而享元模式只是为了复用。

总结

应用享元模式前应该仔细测试是否真的在业务场景中能节省大量内存,否则可能适得其反。

优点

  1. 享元模式在对象被密集使用,并且内容不变时能在多处共享节省大量内存

缺点

  1. 对垃圾回收不友好,因为共享的对象一保有引用不会释放。
  2. 如果对象的生命周期很短并且不会被密集使用,使用享元模式可能占用更多的内存。

相关文章:

【设计模式】结构型设计模式之 享元模式

文章目录 介绍关键概念 应用举例象棋游戏共享棋子对象文本编辑器中文字格式设计成享元模式 享元模式在 Java 中的应用享元模式在包装类缓存中的应用享元模式在 String 中的应用 对比享元模式和单例模式的区别享元模式与缓存的区别 总结优点缺点 介绍 享元模式&#xff0c;”享…...

嵌入式操作系统_5.存储管理

1.存储管理 存储管理是嵌入式操作系统的基本功能之一。其管理的对象是主存&#xff0c;也称内存。它的主要功能包括分配和回收主存空间、提高主存利用率、扩充主存、对主存信息实现有效保护。存储器管理的目的就是提供一个有价值的内存抽象&#xff0c;其目标包括&#xff1a;…...

HTML DOM 事件

HTML DOM 事件 HTML DOM(文档对象模型)事件是当网页中的某些操作发生时,浏览器会自动触发或通过脚本代码手动触发的动作。这些事件可以是对用户操作的响应,如点击按钮,也可以是浏览器自身的动作,如页面加载完成。理解和掌握DOM事件对于前端开发至关重要,因为它们是实现…...

有没有硅基生命?AGI在哪里?

摘要 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;和生命科学的探索逐渐成为人们关注的焦点。其中&#xff0c;关于硅基生命的可能性与AGI&#xff08;Artificial General Intelligence&#xff0c;即人工通用智能&#xff09;的实现&#xff0c;更是引…...

HAL库开发--串口

知不足而奋进 望远山而前行 目录 文章目录 前言 学习目标 学习内容 开发流程 串口功能配置 串口功能开启 串口中断配置 串口参数配置 查询配置结果 发送功能测试 中断接收功能测试 printf配置 DMA收发 配置 DMA发送 DMA接收(方式1) DMA接收(方式2) 总结 前言…...

Web前端设计毕业论文:深度探索与未来展望

Web前端设计毕业论文&#xff1a;深度探索与未来展望 在数字化时代&#xff0c;Web前端设计作为互联网应用的重要组成部分&#xff0c;其重要性和复杂性日益凸显。本论文旨在深度探索Web前端设计的关键要素、发展趋势以及面临的挑战&#xff0c;为未来的研究和实践提供有价值的…...

JAVA 字节运算 取低5位 获取低位第一位

1、JAVA 取低5位 什么是取低5位 在计算机中&#xff0c;每个数字都是以二进制形式存储的。一个二进制数字可以由多个位组成&#xff0c;每一位都可以是 0 或者 1。取低5位即表示只取二进制数字的最后5位&#xff08;从右向左数&#xff09;。 取低5位的方法 在 JAVA 中&#…...

全网首发:教你如何直接用4090玩转最新开源的stablediffusion3.0

1.stablediffusion的概述&#xff1a; Stable Diffusion&#xff08;简称SD&#xff09;近期的动态确实不多&#xff0c;但最新的发展无疑令人瞩目。StableCascade、Playground V2.5和Stableforge虽然带来了一些更新&#xff0c;但它们在SD3面前似乎略显黯然。就在昨晚&#x…...

智慧监狱技术解决方案

1. **建设背景**&#xff1a;介绍了智慧监狱建设的战略部署&#xff0c;包括司法部提出的“数字法治、智慧司法”信息化体系建设&#xff0c;以及智慧监狱建设的总体目标、重点任务和实施步骤。 2. **建设需求**&#xff1a;分析了当前监狱系统存在的问题&#xff0c;如子系统…...

QT——事件

一、什么是事件 在QT中,事件(Event)是指由特定对象发生的动作或状态变化,通常用于响应用户的操作。事件可以是鼠标点击、键盘输入、窗口移动等用户操作,也可以是系统发出的信号,比如定时器超时、网络数据到达等。在QT中,可以通过连接信号与槽(Signals and Slots)的方…...

【SpringBoot】Spring Boot 中高级特性详解

文章目录 1. 异步处理1.1 什么是异步处理&#xff1f;1.2 实现异步处理1.2.1 启用异步支持1.2.2 使用 Async 注解1.2.3 调用异步方法 2. 安全管理2.1 Spring Security 集成2.2 基础安全配置2.2.1 添加依赖2.2.2 默认配置2.2.3 自定义用户认证 3. 监控和调试3.1 Spring Boot Act…...

MQTT TCP HTTP 协议对比

目录 1. 类型与用途 2. 通信模式与特性 3. 优缺点 4. 使用场景 MQTT、TCP和HTTP在类型、用途、通信模式、特性以及使用场景等方面存在显著的区别&#xff0c;以下是详细的阐述&#xff1a; 1. 类型与用途 MQTT&#xff1a;MQTT是一种消息传输协议&#xff0c;主要适用于物…...

C++面向对象程序设计 - 函数库

C语言程序中各种功能基本上都是由函数来实现的&#xff0c;在C语言的发展过程中建立了功能丰富的函数库&#xff0c;C从C语言继承了些函数功能。如果要用函数库中的函数&#xff0c;就必须在程序文件中包含文件中有关的头文件&#xff0c;在不同的头文件中&#xff0c;包含了不…...

computeIfAbsent是Java 8引入的Map接口中的一个方法

computeIfAbsent是Java 8引入的Map接口中的一个方法&#xff0c;它提供了一种更高效且线程安全的方式来 conditionally compute or retrieve a value for a given key in a map. 当你想要为一个键计算一个值&#xff08;如果该键尚不存在对应的映射关系&#xff09;&#xff0c…...

HTML实现进度条/加载框模版

HTML加载 一、环形加载 1二、环形加载 2三、波形加载四、百分比环形五、进度条 一、环形加载 1 <div class"loader"></div>.loader {border: 16px solid #f3f3f3;border-radius: 50%;border-top: 16px solid #3498db;width: 120px;height: 120px;-webki…...

Python 3 列表

Python 3 列表 Python 3 中的列表是一种基本的数据结构,用于存储一系列有序的元素。列表是可变的,这意味着可以修改其内容。在 Python 中,列表是非常灵活和强大的,广泛用于各种编程任务。 创建列表 创建列表非常简单,只需将元素用逗号分隔,并包围在方括号 [] 内。例如…...

Type-C接口显示器:C口高效连接与无限可能 LDR

Type-C显示器C接口的未来&#xff1a;高效连接与无限可能 随着科技的飞速发展&#xff0c;我们的日常生活和工作中对于高效、便捷的连接方式的需求日益增加。在这样的背景下&#xff0c;Type-C接口显示器凭借其卓越的性能和广泛的兼容性&#xff0c;正逐渐崭露头角&#xff0c…...

微服务SpringCloud ES分布式全文搜索引擎简介 下载安装及简单操作入门

Elasticsearch ES简介 分布式全文搜索引擎 我们天天在用ES 搜索的时候 要与多个信息进行匹配查找 然后返回给用户 首先 ES会将数据库中的信息 先进行一个拆分 这个叫做分词 是按照词语关键词拆的 然后就能进行搜索的时候匹配对应的id 每一个关键字对应若干id 每一个…...

护眼灯落地的好还是桌面的好?落地护眼灯性价比高的品牌推荐

护眼灯落地的好还是桌面的好&#xff1f;当我们为了更好地保护眼睛而选择护眼灯时&#xff0c;常常会面临一个纠结的问题&#xff1a;到底是护眼灯落地的好还是桌面的好呢&#xff1f;这看似是一个简单的二选一&#xff0c;实则背后蕴含着诸多需要深入探讨的因素。 护眼灯的选择…...

计算机网络-子网掩码的计算

计算机网络中的子网掩码计算及相关知识 在计算机网络中&#xff0c;子网掩码是一个非常重要的概念。它不仅帮助我们区分网络地址和主机地址&#xff0c;还在网络划分、管理和安全中发挥着重要作用。本文将介绍子网掩码的基本概念、计算方法及其在网络中的应用。 子网掩码的基…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...