重修设计模式-结构型-享元模式
重修设计模式-结构型-享元模式
复用不可变对象,节省内存
享元模式(Flyweight Pattern)核心思想是通过共享对象方式,达到节省内存和提高性能的目的。享元对象需是不可变对象,因为它会被多处代码共享使用,要避免一处代码对享元进行了修改,影响到其他使用它的代码。
享元模式的实现非常简单,主要是通过工厂模式,在工厂类中,通过一个 Map 或者 List 来缓存已经创建过的享元对象,来达到复用的目的。
举个例子,在线象棋游戏:
象棋可同时容纳上万个房间同时进行游戏,每个房间最基础的是象棋和棋盘。一副象棋有32个棋子,如果后每个房间都创建相同的棋子对象,就是千万级别的,对任何系统都是个挑战。
分析这个例子,棋子的字和颜色是固定的几个,跟场景无关;场景相关的只有只有棋子的坐标。那其实可以把棋子不变的部分抽取出来,设计为享元对象,让所有棋子共享,从而让每个棋子对象变得更轻量。
未使用享元模式的棋子对象:
//棋子
data class ChessPieceOld(val id: Long,val text: String,val color: ChessPieceUnit.Color,val positionX: Int,val positionY: Int
) {
}
当对象大量创建时,几个固定的属性(id、text、color)会大量重复,冗余占用内存。这时可以将这几个属性抽出,设计为享元类,并使用带缓存的工厂来生成享元对象:
//棋子固定信息-享元类
data class ChessPieceUnit(val id: Long, val text: String, val color: Color) {enum class Color {RED, BLACK}
}//生成棋子-带缓存的创建工厂
class ChessPieceFactory {companion object {//享元对象池private val chessPiece = hashMapOf(1 to ChessPieceUnit(1, "車", ChessPieceUnit.Color.RED),2 to ChessPieceUnit(2, "馬", ChessPieceUnit.Color.BLACK),//...)fun getChessPiece(id: Int): ChessPieceUnit {return chessPiece[id]!!}}
}//棋子
data class ChessPiece(val piece: ChessPieceUnit, val positionX: Int, val positionY: Int) {
}//棋盘
class ChessBoard() {private val chessPieces: HashMap<Int, ChessPiece> = hashMapOf()fun init() {chessPieces.put(1, ChessPiece(ChessPieceFactory.getChessPiece(1), 0, 1))chessPieces.put(2, ChessPiece(ChessPieceFactory.getChessPiece(1), 0, 1))//...}fun move(chessPieceId: Int, positionX: Int, positionY: Int) {//...}
}
使用享元后,所有棋子的固定部分共同引用数量有限的享元对象,每个棋子对象变得更为轻量,内存中存储的冗余信息大大减少,避免了大量相似对象的开销,提高了系统资源的利用率。
注意上面的例子,使用享元模式后,棋子对象的数量并没有变化,只是对象比之前小了很多。消耗内存最多的成员变量已经被移动到很少的几个享元对象中了,这几个享元对象会被上千个情境小对象复用,无需再重复存储相同数据。
享元模式在 Java 语言中的应用
1.Java 中的字符串常量池
String 类会利用享元模式来复用相同的字符串常量,当某个字符串常量第一次被用到的时候,存储到常量池中,之后再用到的时候,直接引用常量池中已经存在的即可,不需要重复创建对象,通过下面代码可以验证。
//字符串常量池
String s1 = "秋意浓";
String s2 = "秋意浓";
String s3 = new String("秋意浓");//直接new出对象,绕过了Java的常量池优化
System.out.println(s1 == s2); //输出:true
System.out.println(s1 == s3); //输出:false
2.基本类型包装类
基本类型的包装类(如Integer 、Long、Short、Byte 等),也都利用了享元模式来缓存 -128 到 127 之间的数据。因为对于大部分应用来说这个区间是最常用的数值,如果预先创建所有值的享元对象不仅会占用大量内存,也会让类加载时间过长。
以 Integer 类型为例:
//包装类型的享元
Integer n1 = 1;
Integer n2 = 1;
Integer n3 = 129;
Integer n4 = 129;
System.out.println(n1 == n2); //输出:true
System.out.println(n3 == n4); //输出:false
因为 129 超出了享元对象池区间,所以每次会返回新的对象,两个对象地址不同,输出 false。Integer 享元部分源码如下:
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}
private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}
}
可以通过 JDK 参数修改缓存池上限(下限不支持修改):
//方法一:
-Djava.lang.Integer.IntegerCache.high=255
//方法二:
-XX:AutoBoxCacheMax=255
在使用 Java 进行编码时,要尽量避免直接 new 出包装类型或者字符类型对象,如 String s = new String("aaa")、Integer n = new Integer(1),这样会绕过系统的享元模式,创建重复对象。推荐直接使用自动装箱语法,如:Integer n = 1,或通过类型工厂的方式:Integer n = Integer.valueof(1)
上面两个场景都是享元模式在 Java 中的应用,只是实现略有不同:Integer 类中要共享的对象,是在类加载的时候就一次性创建好的;String 类的享元,是在某个字符串第一次被用到的时候,才创建并存储到常量池中的,相当于懒加载方式。
享元模式的类似设计
1.享元模式 vs 单例
虽然享元模式的实现和单例的变体多例非常相似,但它们的设计意图不同:享元模式是为了对象复用,节省内存。而多例是为了限制对象的个数。
2.享元模式 vs 对象池
虽然享元模式和对象池都是为了复用,但他们的“复用”也是不同的概念:
- 对象池中的“复用”可以理解为“重复使用”,使用时被使用者独占,使用完成后放回池中,主要目的是节省时间。
- 享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。
总结
当一个系统中存在大量重复对象的时候,我们就可以利用享元模式,将对象设计成享元,在内存中只保留一份实例,供多处代码引用,这样可以减少内存中对象的数量,以起到节省内存的目的。
相关文章:
重修设计模式-结构型-享元模式
重修设计模式-结构型-享元模式 复用不可变对象,节省内存 享元模式(Flyweight Pattern)核心思想是通过共享对象方式,达到节省内存和提高性能的目的。享元对象需是不可变对象,因为它会被多处代码共享使用,要避…...
JavaScript 运算符
JavaScript 中的运算符可以根据其功能和用途分为几类。以下是主要的运算符类型及其用法: 1. 算术运算符 用于执行基本的数学运算。 : 加法 let sum 5 3; // 8- : 减法 let difference 5 - 3; // 2* : 乘法 let product 5 * 3; // 15/ : 除法 let quotient 5…...
3.js - 运动曲线
这个球,绕着这个红色的线圈转 代码 import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControlslet scene,camera,renderer,controls nulllet moon,earth null// 根据,一系列的点,创建曲线 le…...
免费ppt模板哪里找?职场必备这些利器
一眨眼,9月份的尾声渐近,无论是学生还是职场人士,都开始准备着新一轮的演讲和报告。在这个忙碌的时期,一份精美的PPT模板能够大幅提升你的工作效率,让你的演示更加引人入胜。 不用担心高昂的版权费用,市场…...
wampserve 配置本地域名,出现错误
概述 今天更换了电脑,在本地安装和配置docker的时候,想用自定义域名访问NGINX容器,127.0.0.1和localhost都可以访问,但是自定义域名无法访问, 接着去捯饬已经使用的wampserver的集成环境,出现了同样的问题…...
MySQL慢查询优化指南
博客主页: 南来_北往 系列专栏:Spring Boot实战 前言 当遇到慢查询问题时,不仅影响服务效率,还可能成为系统瓶颈。作为一位软件工程师,掌握MySQL慢查询优化技巧至关重要。今天,我们就来一场“数据库加速之旅…...
怎么录制游戏视频?精选5款游戏录屏软件
对于热爱游戏的你来说,记录游戏中的精彩瞬间并分享给朋友或粉丝,无疑是一种享受。然而,在众多录屏软件中,如何选择最适合你的那一款?今天,我们就为大家精选了五款游戏录屏软件,需要的朋友快来选…...
论文阅读 - MDFEND: Multi-domain Fake News Detection
https://arxiv.org/pdf/2201.00987 目录 ABSTRACT INTRODUCTION 2 RELATED WORK 3 WEIBO21: A NEW DATASET FOR MFND 3.1 Data Collection 3.2 Domain Annotation 4 MDFEND: MULTI-DOMAIN FAKE NEWS DETECTION MODEL 4.1 Representation Extraction 4.2 Domain Gate 4.…...
LabVIEW软件出现Bug如何解决
在LabVIEW开发中,程序出现bug是不可避免的。无论是小型项目还是复杂系统,调试与修复bug都是开发过程中的重要环节。下文介绍如何有效解决LabVIEW软件中的bug,包括常见错误类型、调试工具、错误处理机制。 1. 常见Bug类型分析 在LabVIEW中&am…...
【数据结构-栈】力扣844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。 注意:如果对空文本输入退格字符,文本继续为空。 示例 1: 输入:s “ab#c”, t “…...
DataFrame生成excel后为什么多了一行数字
问题描述 python查询数据生成excel文件,生成的excel多了第一行数字索引,1,2,3,4,5...... 代码: df pd.DataFrame(data)df.to_excel(filename, sheet_name用户信息表, indexFalse) 解决: 原理也很简单,就是设置个参…...
linux 内存屏障(barrier)分析
谈起内存屏障,大家感觉这个"玩意儿"很虚,不太实际,但是内核代码中又广泛地可以看到起身影。内存屏障,英文barrier,这个"玩意儿"它还不太好去定义它。barrier,中文翻译为栅栏,栅栏大家都见过,现实生活中就是防止他人或者动物非法闯入而用来进行隔…...
【人工智能】Transformers之Pipeline(十九):文生文(text2text-generation)
目录 一、引言 二、文生文(text2text-generation) 2.1 概述 2.2 Flan-T5: One Model for ALL Tasks 2.3 pipeline参数 2.3.1 pipeline对象实例化参数 2.3.2 pipeline对象使用参数 2.3.3 pipeline返回参数 …...
如何使用ssm实现基于VUE的儿童教育网站的设计与实现+vue
TOC ssm676基于VUE的儿童教育网站的设计与实现vue 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全…...
MODBUS TCP 转 CANOpen
产品概述 SG-TCP-COE-210 网关可以实现将 CANOpen 接口设备连接到 MODBUS TCP 网络中。用户不需要了解具体的 CANOpen 和 Modbus TCP 协议即可实现将CANOpen 设备挂载到 MODBUS TCP 接口的 PLC 上,并和 CANOpen 设备进行数据交互。 产品特点 …...
vue2+elementUI实现handleSelectionChange批量删除-前后端
功能需求:实现选中一个或多个执行批量删除操作 在elementUI官网选择一个表格样式模板,Element - The worlds most popular Vue UI framework 这里采用的是 将代码复制到前端,这里是index.vue <template><el-button type"dang…...
LLMs之OCR:llm_aided_ocr(基于LLM辅助的OCR项目)的简介、安装和使用方法、案例应用之详细攻略
LLMs之OCR:llm_aided_ocr(基于LLM辅助的OCR项目)的简介、安装和使用方法、案例应用之详细攻略 目录 llm_aided_ocr的简介 1、特性 2、详细技术概览 PDF处理和OCR PDF到图像转换 OCR处理 文本处理流程 分块创建 错误校正与格式化 重复内容移除 标题和页码…...
低代码平台后端搭建-阶段完结
前言 最近又要开始为跳槽做准备了,发现还是写博客学的效率高点,在总结其他技术栈之前准备先把这个专题小完结一波。在这一篇中我又试着添加了一些实际项目中可能会用到的功能点,用来验证这个平台的扩展性,以及总结一些学过的知识。…...
暑假考研集训营游记
文章目录 摘要:1.对各大辅导机构考研封闭集训营的一些个人看法:2.对于考研原因一些感想:结语 摘要: Ashy在暑假的时候参加了所在辅导班的为期一个月的考研封闭集训营,有了一些全新的感悟,略作记录。 1.对…...
C#中的报文(Message)
在C#中,报文(Message)通常是指在网络通信中交换的数据单元。报文可以由多种不同的组成部分构成,具体取决于通信协议和应用场景。 以下是一些常见的报文组成部分: 头部(Header):包含…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
