享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
介绍
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用,尤其是对于大量相似对象的场景。享元模式通常与工厂模式和单例模式结合使用,从而有效地控制和复用对象的创建。在享元模式中,享元对象的核心思想是将不可变的部分(共享的状态)和可变的部分(外部状态)进行区分,从而优化系统性能。
工厂模式与单例模式
享元模式与工厂模式紧密结合,享元工厂负责管理共享的享元对象。工厂模式使得创建和管理享元对象变得统一和高效。除此之外,单例模式也常用于确保享元工厂在整个系统中唯一,避免重复创建享元工厂实例,从而节省资源。
下面代码采用静态内部类的方式实现单例模式,这种方式不仅能保证线程安全,还能确保只有在首次使用时才创建实例,具有懒加载特性。但是枚举才是最优的单例模式实现。感兴趣的同学可以参考这篇文章单例模式最优解----枚举
享元模式的结构
享元模式的核心结构包含以下几个角色:
-
抽象享元角色(Flyweight):这是一个接口或抽象类,声明了享元对象的公共方法。享元对象可以通过这些方法向外界提供共享的数据,同时也可以设置外部数据。
-
具体享元角色(Concrete Flyweight):实现了抽象享元接口,提供了共享的内部状态。在享元工厂中,这些具体享元对象可以通过共享池来复用。
-
非共享的具体享元角色(Unshared Flyweight):这些对象不适合共享,通常包含外部状态。每次使用时都会直接实例化。
-
享元工厂角色(Flyweight Factory):负责管理和提供享元对象。享元工厂会检查是否已经有符合要求的享元对象,如果没有则创建新的对象并返回。
享元模式中的共享与非共享状态
享元模式中有两种主要状态:
-
内部状态:这些状态是享元对象的一部分,并且是共享的。内部状态不会随着环境的改变而改变。
-
外部状态:这些状态是依赖于外部环境的,通常随时间、位置等变化。外部状态不会存储在享元对象中,而是通过方法传递给享元对象。
享元模式的关键在于将这两种状态进行分离,只有内部状态才能被共享,外部状态则由客户端负责传递。
示例:图形编辑器中的享元模式

在这个例子中,我们设计了一个简单的图形编辑器,其中有两种共享形状:圆形和正方形。每个图形的颜色作为内部状态共享,而位置作为外部状态传递。通过享元模式,我们可以复用相同颜色的图形对象,并为每个图形传递不同的位置。如果没有采用共享的方式,每次调用图形时都会创建新的对象,这样会导致大量内存浪费。但位置不能复用,这是根据外部环境改变的。
代码实现
// 抽象享元角色
interface Shape {void draw(String externalState);
}// 具体享元类 - 圆形,内部状态是共享的
class Circle implements Shape {private String color; // 内部状态,颜色是共享的public Circle(String color) {this.color = color;}@Overridepublic void draw(String externalState) {System.out.println("Drawing Circle with color: " + color + " at position: " + externalState);}
}// 具体享元类 - 正方形,内部状态是共享的
class Square implements Shape {private String color; // 内部状态,颜色是共享的public Square(String color) {this.color = color;}@Overridepublic void draw(String externalState) {System.out.println("Drawing Square with color: " + color + " at position: " + externalState);}
}// 非共享的具体享元 - 线条,完全依赖外部状态
class Line implements Shape {@Overridepublic void draw(String externalState) {System.out.println("Drawing Line at position: " + externalState);}
}// 享元工厂
class ShapeFactory {private Map<String, Shape> shapeMap = new HashMap<>();private ShapeFactory() {} // 私有构造函数,防止外部创建实例// 静态内部类实现单例模式private static class SingletonHelper {private static final ShapeFactory INSTANCE = new ShapeFactory();}// 获取单例实例public static ShapeFactory getInstance() {return SingletonHelper.INSTANCE;}// 获取共享的享元对象public Shape getShape(String type, String color) {String key = type + color;if (!shapeMap.containsKey(key)) {if (type.equals("Circle")) {shapeMap.put(key, new Circle(color));} else if (type.equals("Square")) {shapeMap.put(key, new Square(color));}}return shapeMap.get(key);}
}public class FlyweightPatternExample {public static void main(String[] args) {// 享元工厂ShapeFactory shapeFactory = ShapeFactory.getInstance();// 创建和使用共享的享元对象Shape circle1 = shapeFactory.getShape("Circle", "Red");Shape circle2 = shapeFactory.getShape("Circle", "Red");Shape square1 = shapeFactory.getShape("Square", "Blue");// 绘制图形时,外部状态(位置)会变化circle1.draw("10, 20"); // 外部状态为位置circle2.draw("15, 25");square1.draw("30, 40");// 非共享的享元对象(直接实例化)Shape line = new Line();line.draw("50, 60"); // 外部状态为位置}
}
代码说明
-
共享的具体享元类:
Circle和Square是共享的享元对象,它们的颜色是内部状态。享元工厂会根据颜色来共享这些对象。
-
非共享的具体享元类:
Line类是非共享的享元类,它不存储任何内部状态,而是每次都需要通过外部状态(位置)来绘制。它没有参与享元工厂的共享池。
-
享元工厂:
ShapeFactory类负责管理和返回共享的享元对象。它使用一个Map来缓存已创建的享元对象,避免重复创建相同的对象。
-
客户端使用:
- 客户端首先通过享元工厂请求共享的图形对象(如红色圆形)。如果该对象已存在,则直接返回;否则,创建一个新的对象。
- 对于非共享的对象(如线条),客户端直接实例化,而不通过享元工厂。
总结
享元模式通过共享对象的方式有效减少了内存使用,尤其适用于对象数量庞大且状态相似的场景。JDK中也有享元模式的应用,感兴趣的同学可以往这看---->JDK享元模式的运用
相关文章:
享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
介绍 享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用,尤其是对于大量相似对象的场景。享元模式通常与工厂模式和单例模式结合使用,从而有效地控制和复用对象的创建。在享元模式中&am…...
【物联网技术】ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——UDP数据透传
前言:完成ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——实现UDP数据透传 STA模式,通俗来说就是模块/单片机去连接路由器/热点来通信。 UDP协议,是传输层协议,UDP没有服务器和客户端的说法。 本实验需要注意,wifi模块/单片机与电脑/手机需要连接在同一个…...
【SQL Server】华中农业大学空间数据库实验报告 实验一 数据库
实验目的 熟悉了解掌握SQL Server软件的基本操作与使用方法,认识界面,了解其两个基本操作系统文件,并能熟练区分与应用交互式与T-SQL式两种方法在SQL Server中如何进行操作;学习有关数据库的基本操作,包括:…...
操作系统页面置换算法Java实现(LFU,OPT,LRU,LFU,CLOCK)
FIFO先进先出算法 java import java.util.LinkedList; import java.util.Queue; public class Main { //先进先出的思想 是 用一个队列去模拟数据 如果当前不存在就是发生缺页中断了 就需要添加 如果已经满了 将队头的元素出队 即可 //先进先出 就是一个数组 frameCount publi…...
Request和Response
前言 这一节主要讲的是Request和Response还有一些实例 1. 介绍 就是这两个参数 WebServlet("/demo7") public class ServletDemo7 extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletExcepti…...
【青牛科技】GC8549替代LV8549/ONSEMI在摇头机、舞台灯、打印机和白色家电等产品上的应用分析
引言 在现代电子产品中,控制芯片的性能直接影响到设备的功能和用户体验。摇头机、舞台灯、打印机和白色家电等领域对控制精度、功耗和成本等方面的要求日益提高。LV8549/ONSEMI等国际品牌的芯片曾是这些产品的主要选择,但随着国内半导体技术的进步&…...
(十二)JavaWeb后端开发——MySQL数据库
目录 1.数据库概述 2.MyQSL 3.数据库设计 DDL 4.MySQL常见数据类型 5.DML 1.数据库概述 数据库:DataBase(DB),是存储和管理数据的仓库 数据库管理系统:DataBase ManagementSystem(DBMS),操纵和管理数据库的大型软件 SQL&a…...
pnpm管理多工作区依赖
pnpm是一个支持多包仓库的一个包管理工具,那么怎么可以在项目根目录下执行pnpm install的时候,也能同步让所有的工作区都能够通安装依赖呢? 方式一,在执行pnpm install指令的时候,添加recursive参数: pnpm install --recursive 方式二,在项目的根目录下通过pnpm的配置文件p…...
如何在本地Linux服务器搭建WordPress网站结合内网穿透随时随地可访问
文章目录 前言1. 安装WordPress2. 创建WordPress数据库3. 安装相对URL插件4. 安装内网穿透发布网站4.1 命令行方式:4.2. 配置wordpress公网地址 5. 配置WordPress固定公网地址 前言 本文主要介绍如何在Linux Ubuntu系统上使用WordPress搭建一个本地网站,…...
二、应用层,《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》
文章目录 零、前言一、应用层协议原理1.1 网络应用的体系结构1.1.1 客户-服务器(C/S)体系结构1.1.2 对等体(P2P)体系结构1.1.3 C/S 和 P2P体系结构的混合体 1.2 进程通信1.2.1 问题1:对进程进行编址(addressing)&#…...
面粉直供系统|基于java和小程序的食品面粉直供系统设计与实现(源码+数据库+文档)
面粉直供系统 目录 基于java和小程序的食品面粉直供系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师&#x…...
十四:java web(6)-- Spring Spring MVC
目录 Spring MVC 1.1 Spring MVC 概述 1.1.1 什么是 MVC 模式 1.1.2 Spring MVC 工作原理 1.2 Spring MVC 核心组件 1.2.1 DispatcherServlet 1.2.2 控制器(Controller) 1.2.3 请求映射(RequestMapping) 1.2.4 视图解析器…...
Java代码实现策略模式处理支付付款业务
1.需求:因为付款功能集成的第三方支付SDK越来越来多不好维护,改用策略模式实现,来代替代码中多余的if else 判断。 2.什么是策略模式? 策略模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为。该模式将不同的算法封装成独立的策略类,并使这些…...
unity3d————四元数概念
一、定义与表示 四元数是由一个实数部分和三个虚数部分组成,通常表示为q w xi yj zk,其中w是实数,x、y、z是实数系数,i、j、k是虚数单位,满足以下关系: i j k -1ij k,ji -kjk i&…...
spring相关的面试题
Spring 框架是 Java 开发中最常用的框架之一,因此在面试中经常会被问到与 Spring 相关的问题。以下是一些常见的 Spring 面试题及其答案。 基础概念 什么是 Spring 框架? Spring 框架是一个开源的 Java 平台,用于构建企业级应用程序。它提供…...
STM32外设之SPI的介绍
### STM32外设之SPI的介绍 SPI(Serial Peripheral Interface)是一种高速的,全双工,同步的通信总线,主要用于EEPROM、FLASH、实时时钟、AD转换器等外设的通信。SPI通信只需要四根线,节约了芯片的管脚&#x…...
二十三、Mysql8.0高可用集群架构实战
文章目录 一、MySQL InnoDB Cluster1、基本概述2、集群架构3、搭建一主两从InnoDB集群3.1、 安装3个数据库实例3.2、安装mysqlrouter和安装mysqlshell3.2.1、安装mysql-router3.2.2、安装mysql-shell 3.3、InnoDB Cluster 初始化3.3.1、参数及权限配置预需求检测3.3.2、初始化I…...
docker file 精简规则
在编写 Dockerfile 时,精简规则不仅有助于减小镜像大小,还能提高构建速度和可维护性。以下是一些常见的精简 Dockerfile 规则: 1. 尽量合并 RUN 指令 每个 RUN 指令会产生一个新的镜像层,因此多个命令可以合并为一个 RUN 指令&a…...
前端加密方式详解与选择指南
在当今数字化时代,前端数据安全的重要性日益凸显。本文将深入探讨前端加密的多种方式,为你提供选择适合项目加密方式的实用策略,并分享一些实际案例及相应代码。 一、前端加密方式汇总 (一)HTTPS 加密 HTTPS 是在 H…...
【React】条件渲染——逻辑与运算符
条件渲染——逻辑与&&运算符 你会遇到的另一个常见的快捷表达式是 JavaScript 逻辑与(&&)运算符。在 React 组件里,通常用在当条件成立时,你想渲染一些 JSX,或者不做任何渲染。 function Item({ nam…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
