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

文章目录

  • 介绍
      • 关键概念
  • 应用举例
      • 象棋游戏共享棋子对象
      • 文本编辑器中文字格式设计成享元模式
  • 享元模式在 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. 如果对象的生命周期很短并且不会被密集使用,使用享元模式可能占用更多的内存。

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

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

相关文章

简单用java集合模拟斗地主发牌操作

简易斗地主发牌&#xff08;熟悉java集合的使用&#xff09; 1、需求 按照斗地主规则&#xff0c;完成洗牌发牌的动作。 具体要求如下&#xff1a; 1、准备牌&#xff1a;组装54张扑克牌 2、洗牌&#xff1a;54张牌顺序打乱 3、发牌&#xff1a;三个玩家参与游戏&#xf…

TinyVision V851s 使用 OpenCV + NPU 实现 Mobilenet v2 目标分类识别

用39块钱的V851se视觉开发板做了个小相机。 可以进行物品识别、自动追焦&#xff01; 这个超低成本的小相机是在V851se上移植使用全志在线开源版本的Tina Linux与OpenCV框架开启摄像头拍照捕获视频&#xff0c;并结合NPU实现Mobilenet v2目标分类识别以及运动追踪等功能......并…

以无厚,入有间,做一件事为什么靠努力不行,不能长期维持

庖丁解牛&#xff0c;并不是在说人和技巧&#xff0c;而是在说解牛不在于刀的锋利&#xff0c;而是怎样才能做到让刀不产生损耗&#xff0c;就是熟悉牛肉纹路&#xff0c;按照纹路和肉骨间隙进行操刀。这就是尊重自然规律&#xff0c;对于人也是一样的&#xff0c;如果所有事情…

C++输入输出与IO流

C 输入输出与I/O流 文章目录 C 输入输出与I/O流IO类型与基础特性概念与特性IO状态输出缓冲区 文件输入输出文件模式 string流IO处理中常用的函数及操作符综合练习与demo一、 创建文件并写入二、控制台输入数据并拆分存储三、读写电话簿 IO类型与基础特性 C11标准提供了几种IO处…

el-date-picker设置结束时间为23:59:59

type datetime <el-date-pickerstyle"width: 100%"v-model"currentEditConfigObj.expirationDate"placeholder"请选择结束时间"type"datetime"default-time[23:59:59]value-format"yyyy-MM-dd HH:mm:ss"format"yy…

Python使用Flask构建简单的web应用

构建一个简单的 Flask Web 应用程序是学习 Python Web 开发的良好起点。Flask 是一个轻量级的 WSGI Web 应用框架&#xff0c;它的主要目标是让开发者更容易构建 Web 应用&#xff0c;同时保持简单性和灵活性。下面我们将详细介绍如何使用 Flask 构建一个简单的 Web 应用&#…

一个支持多存储的文件列表/WebDAV程序,使用 Gin 和 Solidjs

网址 https://github.com/alist-org/alist 看了下介绍&#xff0c;支持挺多oss和云盘的&#xff0c;看了下代码&#xff0c;值得学习一下 部署方便&#xff0c;开箱即用 文件预览&#xff08;PDF、markdown、代码、纯文本……&#xff09; 画廊模式下的图像预览 视频和音频预…

android 抓取 logcat 日志的方法

1.找到这个路径 2.然后执行命令&#xff08;adb logcat -v time >.\\logcat.log&#xff09;&#xff0c;开始抓取日志 3.这个时候就可以去操作APP了&#xff0c;复现BUG了。 Ctrlc 结束日志抓取 adb logcat -c 清空旧日志

逆市创新高!水电“双雄“是怎样炼成的? 博通,赢麻了!

高分红夏季用电高峰AI的尽头是电力 6月7日&#xff0c;长江电力&#xff08;600900&#xff09;、华能水电&#xff08;600025&#xff09;股价双双上涨。截至收盘&#xff0c;长江电力股价上涨1%&#xff0c;收于28.31元/股&#xff1b;华能水电股价上涨1.52%&#xff0c;收于…

Matlab|遗传粒子群-混沌粒子群-基本粒子群

目录 1 主要内容 2 部分代码 3 效果图 4 下载链接 1 主要内容 很多同学在发文章时候最犯愁的就是创新点创新点创新点&#xff08;重要的事情说三遍&#xff09;&#xff0c;对于采用智能算法的模型&#xff0c;可以采用算法改进的方式来达到提高整个文章创新水平的目的&…

基于STM32开发的智能机器人导航系统

目录 引言环境准备智能机器人导航系统基础代码实现&#xff1a;实现智能机器人导航系统 4.1 距离传感器数据读取4.2 电机控制4.3 实时路径规划与避障4.4 用户界面与状态显示应用场景&#xff1a;智能机器人导航与控制问题解决方案与优化收尾与总结 1. 引言 智能机器人导航系…

C++: map和set的使用

目录 1.头文件 2.set和multiset的区别 3.set和map的区别 4.加了multi的区别 这里只说几个要点。 1.头文件 #include <map> #include <set> 2.set和multiset的区别 set和multiset的区别 setmultiset只能插入不重复的key可以插入重复的key一串包含重复的strin…

力扣算法题:轮转数组 -- 多语言实现

无意间看到&#xff0c;力扣存算法代码居然还得升级vip。。。好吧&#xff0c;我自己存吧 其他的题找不回来了……重写吧 golang&#xff1a; func rotate(nums []int, k int) { // 第一想到的 // n : len(nums) // k % n // newNums : nums[n-k:] // numsFeature : …

btstack协议栈实战篇--GAP LE Advertisements Scanner

btstack协议栈---总目录-CSDN博客 目录 1.GAP LE 用于接收广播的设置 2.GAP LE 广告数据转储器 3.HCI packet handler 4.log信息 实现如何扫描和解析广告。 1.GAP LE 用于接收广播的设置 GAP LE 广告作为 GAP EVENT ADVERTISING REPORT的自定义 HCI 事件被接收。要接收…

中国西部AI创新港,数字经济的新引擎

编辑&#xff1a;阿冒 设计&#xff1a;沐由 时间&#xff1a;2024年5月31日。 地点&#xff1a;西安交通大学创新港校区涵英楼千人报告厅。 人物&#xff1a;近千位政府、高校和企事业单位的代表&#xff0c;还有我。 我们的脚下&#xff0c;就是八百里秦川。 三秦自古多才俊。…

Linux基础指令(一)

前言 Linux基础指令主要学习&#xff1a;对目录、文件、压缩包、匹配查找&#xff0c;权限等操作 第一次接触ubuntu需要知道的基本知识 sudo passwd root 先给root用户设置密码 su root 切换到root用户 su zhangsan …

Django里的Form组件

Form组件提供 自动生成HTML标签和半自动读取关联数据 (“半自动”是指还得需要自己手写输入数据进来)表单验证和错误提示 要想创建并使用该组件&#xff0c;操作步骤如下&#xff1a; 在 views.py 里创建类 # 在 views.py 文件里from django import formsclass AssetForm(fo…

core dump核心转储

检查核心转储是否开启&#xff0c;否则无法生成core文件 ulimit -a 如果为0就需要修改 ulimit -c 10240 写一个会触发core命令的程序 以浮点数运算为例 #include <iostream>int main() {int i 1/0; } 在编译时使用-g选项 运行程序&#xff0c;生成core文件 gdb调试 g…

游戏心理学Day09

动机 动机是一个概括性术语&#xff0c;是对所有引起支配和维持心理生理活动的过程的概括 所有生物都有趋向于某些刺激而远离某些刺激&#xff0c;这由它们的本能所决定 通过考虑动机可以解释和预测个体的行为&#xff0c;这显然对于游戏设计来说是件很重要的事情&#xff0…

C++ MPI多进程并发

下载 用法 mpiexec -n 8 $PROCESS_COUNT x64\Debug\$TARGET.exe 多进程并发启动 mpiexec -f hosts.txt -n 3 $PROCESS_COUNT x64\Debug\$TARGET.exe 联机并发进程&#xff0c;其它联机电脑需在相同路径下有所有程序 //hosts.txt 192.168.86.16 192.168.86.123 192.168…