详解享元模式
引言
在计算机中,内存是非常宝贵的资源,而程序中可能会有大量相似或相同的对象,它们的存在浪费了许多空间。而享元模式通过共享这些对象,从而解决这种问题的。
1.概念
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State):
(1)内部状态:存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。
(2)外部状态:随环境改变而改变的、不可以共享的状态。外部状态通常由客户端保存,并在享元对象被创建之后,需要用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。如字符的颜色和字体大小,字符“a”可以是红色的,也可以是绿色的,可以是小四号字体,也可以是三号字体。
区分内部状态和外部状态保证可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
2.模式结构

3.模式分析
Flyweight:抽象享元类,可以是接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
ConcreteFlyweight:具体享元类,它实现了抽象享元类,其实例称为享元对象。在具体享元类中为内部状态提供了存储空间,通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
UnsharedConcreteFlyweight:非共享具体享元类,并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
享元类的设计很重要,核心代码如下:
public class Flyweight {//内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的private String intrinsicState;public Flyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中public void operation(String extrinsicState){//设置外部状态}}
FlyweightFactory:享元工厂类,享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(或其他类型的集合),可以结合工厂模式进行设计。当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。核心代码如下:
public class FlyweightFactory {//定义一个HashMap用于存储享元对象,实现享元池private HashMap<String,Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {//如果对象存在,则直接从享元池获取if (flyweights.containsKey(key)) {return (Flyweight) flyweights.get(key);} else {//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回Flyweight fw = new ConcreteFlyweight();flyweights.put(key, fw);return fw;}}}
4.具体实例分析

Shape:抽象享元类,包含享元类的内部状态shape和外部状态xy坐标。具体代码如下:
//抽象享元类public abstract class {//内部状态是shapepublic abstract String getShape();//外部状态是中心xy坐标public void draw(int x,int y){System.out.println("形状:" + this.getShape() + " 坐标x:" + x + " 坐标y:" + y);}}
Circle:具体享元类,继承抽象享元类Shape,并实现getShape()方法,返回实例的内部状态:形状是圆。具体代码如下:
//具体享元类:圆public class Circle extends Shape{public String getShape(){return "圆";}}
Rectangle:具体享元类,继承抽象享元类Shape,并实现getShape()方法,返回实例的内部状态:形状是矩形。具体代码如下:
//具体享元类:矩形public class Rectangle extends Shape{public String getShape(){return "矩形";}}
ShapeFlyweightFactory:享元工厂类,使用单例模式设计享元工厂类,保证全局只有唯一的工厂实例(懒汉模式实现)。同时创建HashMap作为共享享元类的享元池,保证不重复创建相同或相似的对象。具体代码如下:
//享元工厂类:单例模式public class ShapeFlyweightFactory {//单例模式实现只有一个全局唯一的工厂(懒汉模式)private static ShapeFlyweightFactory shapeFlyweightFactory = new ShapeFlyweightFactory();//定义一个HashMap用于存储享元对象,实现享元池private static HashMap<String,Shape> flyweights = new HashMap<>();//返回唯一实例工厂public static ShapeFlyweightFactory getInstance(){return shapeFlyweightFactory;}public Shape getShapeFlyweight(String shape) {//如果对象存在,则直接从享元池获取if (flyweights.containsKey(shape)) {return flyweights.get(shape);} else {//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回Shape flyweight;if(shape.equals("圆")){flyweight = new Circle();}else if(shape.equals("矩形")) {flyweight = new Rectangle();}else{System.out.println("系统没有这个形状!");return null;}flyweights.put(shape, flyweight);return flyweight;}}}
Client:客户端,创建多个相同对象,观察是否是同一个对象,并给对象添加外部状态。具体代码如下:
public class Client {public static void main(String[] args) {//获得唯一的享元工厂实例ShapeFlyweightFactory shapeFlyweightFactory = ShapeFlyweightFactory.getInstance();//从工厂中获得形状Shape circle1 = shapeFlyweightFactory.getShapeFlyweight("圆");Shape circle2 = shapeFlyweightFactory.getShapeFlyweight("圆");System.out.println("circle1==circle2? " + (circle1 == circle2));Shape rectangle1 = shapeFlyweightFactory.getShapeFlyweight("矩形");Shape rectangle2 = shapeFlyweightFactory.getShapeFlyweight("矩形");System.out.println("rectangle1==rectangle2? " + (rectangle1 == rectangle2));//为形状添加外部状态:颜色circle1.draw(1,2);circle2.draw(2,3);rectangle1.draw(1,3);rectangle2.draw(4,6);}}
运行代码,结果如下:

结果显示,创建多个相同对象,享元模式的背后只会创建一个,并存储在享元池中共享,这样就节省了内存资源。
5.模式扩展
(1)在享元模式的享元工厂类中通常提供一个静态的工厂方法用于返回享元对象,使用简单工厂模式来生成享元对象。
(2)在一个系统中,通常只有唯一一个享元工厂,因此可以使用单例模式进行享元工厂类的设计。
(3)去掉非共享具体享元类,标准的享元模式就演变为单纯享元模式,即单纯享元模式中所有具体享元类都可以共享。
(4)享元模式+组合模式=复合享元模式,统一对多个享元对象设置外部状态。

通过复合享元模式,可以确保复合享元类CompositeConcreteFlyweight中所包含的每个单纯享元类ConcreteFlyweight都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。如果希望为多个内部状态不同的享元对象设置相同的外部状态,可以考虑使用复合享元模式。
(5)Java中String类采用享元模式。
public class StringFlyweight {public static void main(String[] args) {String str1 = "abcd";String str2 = "abcd";String str3 = "ab" + "cd";String str4 = "ab";str4 += "cd";System.out.println(str1 == str2);System.out.println(str1 == str3);System.out.println(str1 == str4);str2 += "e";System.out.println(str1 == str2);}}
这段代码的输出如下:

在Java中,如果每次执行String str1 = “abcd”创建一个新的字符串对象,内存开销将会很大。为此,Java设计了字符串常量池,字符串常量池中存储着唯一的字符串对象。每次创建相同的字符串都是对这唯一对象的引用,因此str1==str2==str3成立。而str4创建时由于初始值是“ab”,进行字符串连接操作Java为其重新分配了内存,创建了一个新的对象“abcd”,因此str4!=str1。而str2再次进行连接操作时,也创建了一个新的对象“abcde”,因此str2此时和str1指向的不是同一个对象。
关于Java String类这种在修改享元对象时,先将原有对象复制一份,然后在新对象上再实施修改操作的机制称为“Copy On Write”。
6.优缺点
主要优点如下:
(1)可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
(2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
主要缺点如下:
(1)享元模式使得系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
(2)为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
7.适用情况
(1)一个系统有大量相同或者相似的对象,造成内存的大量耗费。
(2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
(3)在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
相关文章:
详解享元模式
引言 在计算机中,内存是非常宝贵的资源,而程序中可能会有大量相似或相同的对象,它们的存在浪费了许多空间。而享元模式通过共享这些对象,从而解决这种问题的。 1.概念 享元模式(Flyweight Pattern):运用共享技术有效地…...
第18章 不可变对象设计模式(Java高并发编程详解:多线程与系统设计)
1.线程安全 所谓共享的资源,是指在多个线程同时对其进行访问的情况下,各线程都会使其发生变化,而线程安全性的主要目的就在于在受控的并发访问中防止数据发生变化。除了使用synchronized关键字同步对资源的写操作之外, 还可以在线…...
openEuler22.03LTS系统升级docker至26.1.4以支持启用ip6tables功能
本文记录了openEuler22.03LTS将docker升级由18.09.0升级至26.1.4的过程(当前docker最新版本为27.5.1,生产环境为保障稳定性,选择升级到上一个大版本26的最新小版本)。 一、现有环境 1、系统版本 [rootlocalhost opt]# cat /etc…...
< OS 有关 > Ubuntu 版本升级 实践 24.04 -> 24.10, 安装 .NET
原因: 想安装 .NET 9 去编译 GitHut 项目,这回用不熟悉的 Ubuntu来做,不知道怎么拐去给 Ubuntu 升级,看到现在版本是 24.10 但不是 LTS 版本,记录下升级过程。 一、实践过程: 1. 查看当前版本 命令1: l…...
某咨询大数据解决方案介绍(32页PPT)
本文档介绍了一个大数据平台解决方案,旨在解决企业当前面临的数据问题,包括数据定义缺失、重复采集和存储、数据不完整以及缺乏可靠决策依据等。通过引入大数据技术,该方案强调从被动的IT支撑向主动的数据核心服务转型,以实现科学…...
ZooKeeper作为注册中心有什么问题? ZooKeeper作为注册中心,海量服务同时重启有什么问题?
目录 ZooKeeper作为注册中心存在的问题 性能瓶颈 一致性保证 复杂性 扩展性 单点故障 数据模型限制 社区和生态 安全性 总结 ZooKeeper作为注册中心,海量服务同时重启有的问题 1. ZooKeeper集群压力剧增 2. ZooKeeper Leader节点压力 3. 会话和临时节点管理 4.…...
matlab simulink 汽车四分之一模型主动被动悬架-LQR
1、内容简介 略 matlab simulink 可以交流、咨询、答疑 124- 2、内容说明 略汽车悬架系统由弹性元件、导向元件和减振器组成,是车身与车轴之间连接的所有组合体零件的总称,也是车架(或承载式车身)与车桥(或车轮)之间一切力传递装置的总称,其主要功能是使车轮与地面有很好的…...
从零开始:OpenCV 图像处理快速入门教程
文章大纲 第1章 OpenCV 概述 1.1 OpenCV的模块与功能 1.2 OpenCV的发展 1.3 OpenCV的应用 第2章 基本数据类型 2.1 cv::Vec类 2.2 cv::Point类 2.3 cv::Rng类 2.4 cv::Size类 2.5 cv:&…...
基础相对薄弱怎么考研
复习总体规划 明确目标 选择专业和院校:根据你的兴趣、职业规划和自身实力,选择适合自己的专业和院校。可以参考往年的分数线、报录比、复试难度等。了解考试科目:不同专业考试科目不同,一般包括: 公共课:…...
强化学习笔记6——异同策略、AC、等其他模型总结
异步两种方法:1:经验回放 2:数据动作非同时产生 举例QLearning为什么是异策略? 生成动作时e的概率从Q表选,1-e概况随机。 更新策略时,贪心策略选择Q_max作为动作。 策略优化两种主要方法:基于梯…...
Linux提权--passwd提权
passwd 命令用于更改用户密码。在 Linux 系统中,普通用户可以通过 passwd 更改自己的密码,但如果攻击者能够以某种方式执行 passwd 命令更改 root 用户的密码,他们就能获取 root 权限。 1.常见的 passwd 提权方法 SUID 设置࿱…...
一、本地部署安装 DeepSeek 并训练本地知识库,并调用对话框进行问答
本地部署安装 DeepSeek 1、硬件环境 操作系统:Windows10 内存:16G 显卡:NIVIDIA GeForce RTX 2060 6G 2、安装步骤 (1)安装 Ollama 访问Ollama 官网,点击 “Download for Windows” 下载安装程序。下载…...
海思的一站式集成环境Hispark Studio更新了
HiSpark Studio是海思提供的面向智能设备开发者提供一站式集成开发环境,支持代码编辑、编译、烧录和调试等功能。我以前在评测星闪芯片的时候用过,当时写了篇博客:【星闪开发连载】WS63E开发板Windows环境的构建_hispark studio-CSDN博客。那…...
从零开始构建强大 AI 对话系统:ollama + deepseek + open-webui 完整部署教程(Docker 版)
文章目录 前言一、工具简介二、前期准备三、部署步骤1. 安装并配置 ollama2. 部署 open-webui 四、调试与验证五、Docker Compose 简化部署六、注意事项与常见问题1. ollama run 500 报错2. 硬件配置对性能的影响3. **ollama** 启动与 **open-webui** 调用速度差异4. 内存不足导…...
unity学习29:摄像机camera相关skybox 和 Render Texture测试效果
目录 1 摄像机 1.1 每个Scene里都自带一个摄像机 camera 1.2 可以创建多个camera 1.3 下面先看backgroundtype: 2 backgroundtype: 天空盒 skybox 2.1 清除标志,清除:天空盒 自选天空盒 2.2 window /Asset Store 2.3 导入skybox 3 backgroundtype: 纯色…...
【Elasticsearch】Geo-distance聚合
geo_distance聚合的形状是圆形。它基于一个中心点(origin)和一系列距离范围来计算每个文档与中心点的距离,并将文档分配到相应的距离范围内。这种聚合方式本质上是以中心点为圆心,以指定的距离范围为半径的圆形区域来划分数据。 为…...
音频进阶学习十二——Z变换
文章目录 前言一、Z变换1.Z变换的作用2.Z变换公式3.Z的状态表示1) r 1 r1 r12) 0 < r < 1 0<r<1 0<r<13) r > 1 r>1 r>1 4.关于Z的解释 二、收敛域1.收敛域的定义2.收敛域的表示方式3.ROC的分析1)当 …...
easyxor
easyxor 一、查壳 无壳,64位 二、IDA分析 1.main 2.查看key与r(shifee提取) 三、脚本 r [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, …...
通过多层混合MTL结构提升股票市场预测的准确性,R²最高为0.98
“Boosting the Accuracy of Stock Market Prediction via Multi-Layer Hybrid MTL Structure” 论文地址:https://arxiv.org/pdf/2501.09760 摘要 本研究引入了一种创新的多层次混合多任务学习架构,致力于提升股市预测的效能。此架构融…...
日本游戏机市场5年来首次陷入萎缩;特斯拉招人推进人形机器人量产;任天堂专利显示Switch2手柄可用作鼠标...| 游戏智眼日报
美团成立“算法顾问委员会” 美团宣布,近日,由外部专家学者组成的算法顾问委员会成立,为美团改进算法提供常态化咨询和指导。每个季度美团将举办算法恳谈会,持续邀请骑手、商家、用户、专家学者和媒体代表等共同参加。美团表示&a…...
114-机器学习分类算法
1、内容简介 略 matlab simulink 114-机器学习分类算法可以交流、咨询、答疑 2、内容说明 略 Elong_6.24。ROCAUC confusion newdata Unbalanced_LR.car 3、仿真分析 略 4、参考论文 略...
【论文阅读】On the Security of “VOSA“
On the Security of Verifiable and Oblivious Secure Aggregation for Privacy-Preserving Federated Learning -- 关于隐私保护联邦中可验证与遗忘的安全聚合的安全性 论文来源摘要Introduction回顾 VOSA 方案对VOSA不可伪造性的攻击对于类型 I 的攻击对于类型 II 的攻击 论文…...
12.6 LangChain检索器(Retrievers)全解析:构建高效RAG应用的核心引擎
LangChain检索器(Retrievers)全解析:构建高效RAG应用的核心引擎 一、检索器的核心价值 检索器是大模型应用的智能导航系统,通过将用户查询与知识库精准匹配,解决了传统搜索的三大痛点: 语义鸿沟:突破关键词匹配,理解用户真实意图多源整合:融合向量搜索、关键词搜索和…...
707设计链表(链表操作)
1、题目描述 你可以选择使用单链表或者双链表,设计并实现自己的链表。 单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。 如果是双向链表,则还需要属性 prev 以指示链表中…...
储能系统-系统架构
已更新系列文章包括104、61850、modbus 、单片机等,欢迎关注 IEC61850实现方案和测试-1-CSDN博客 快速了解104协议-CSDN博客 104调试工具2_104协议调试工具-CSDN博客 1 电池储能系统(BESS) 架构 电池储能系统主要包括、电池、pcs、本地控制…...
Spring Boot 需要独立的容器运行吗
Spring Boot 不需要独立的容器运行,它内置了一个嵌入式的Web服务器(如Tomcat、Jetty或Undertow),所以可以直接作为一个独立的应用程序运行,而不需要外部的Servlet容器。你只需要运行Spring Boot应用,它会自…...
Spring Security在java中的详细用处///为什么用了jwt之后就不能用session
Spring Security 是一个功能强大且高度可定制的认证和授权框架,主要用于基于 Spring 的应用程序中。它不仅处理 HTTP 请求的安全性(包括认证和授权),还提供了其他安全相关的特性如防止 CSRF 攻击、会话管理、安全头信息设置等。以…...
Ubuntu系统 Zabbix 7.2LTS一键部署脚本
为了在 Ubuntu 系统上快速部署 Zabbix 7.2 LTS 版本,您可以使用一个自动化 Bash 脚本来简化安装过程。以下是一个适用于 Ubuntu 系统的 Zabbix 7.2 LTS 一键部署脚本。此脚本将安装 Zabbix Server、Zabbix Web 界面(基于 Nginx 和 PHP)以及 Z…...
基于HTML、CSS 和 JavaScript 开发个人读书类网站
以下是一个使用 HTML、CSS 和 JavaScript 开发个人读书类网站的示例代码,包含基本功能和样式: 我的读书空间 我的书库 首页 添加新书 <div class="container"><!-- 首页内容 --><div id="home"><h2>当前阅读列表</h2><…...
Ollama python交互:chat+embedding实践
Ollama简介 Ollama 是一个开源的大型语言模型(LLM)平台,旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型,支持文本生成、翻译、代码编写、问答等…...
