享元模式-提供统一实现对象的复用
下围棋时,分为黑白棋子。棋子都一样,这是出现的位置不同而已。如果将每个棋子都作为一个独立的对象存储在内存中,将导致内存空间消耗较大。我们可以将其中不变的部分抽取出来,只存储它的位置信息来实现节约内存。
图 围棋
1 享元模式概述
通过共享技术实现相同或相似对象重用。做到共享的关键是区分“内部状态”和“外部状态”。
- 内部状态:是存储在享元内部并且不会随着环境改变而改变的状态,内部状态可共享。比如围棋的颜色属性。
- 外部状态:是随着环境改变而改变的、不可共享的状态。外部状态通常由客户端保存,并且在享元对象被创建之后,需要使用的时候,再传入享元对象的内部。一个外部状态与另一个外部状态之间是相互独立的。比如围棋的位置属性。
图 享元模式结构图
Flyweight: 抽象享元类,通常是一个接口或抽象类。声明的方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(状态)。
ConcreteFlyweight: 具体享元类,其实例称为享元对象。为内部状态提供了存储空间。通常可以结合单例模式来设计具体享元类。
UnsharedConcreteFlyweight: 非共享具体享元类。并不是所有抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类。当需要一个非共享具体享元对象时,可以直接通过实例化创建。
FlyweightFactory: 享元工厂类,用于创建并管理享元对象,针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中。
public class Coordinate {private int x;private int y;public Coordinate(int x, int y) {this.x = x;this.y = y;}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}@Overridepublic String toString() {return "{" +"x=" + x +", y=" + y +'}';}
}
public abstract class AbstractChess {public abstract String getColor();public void display(Coordinate coordinate) {System.out.println(getColor() + "落在" + coordinate);}}public class BlackChess extends AbstractChess{@Overridepublic String getColor() {return "黑棋";}private BlackChess() {}private static class ClassHolder {private static final BlackChess instance = new BlackChess();}public static BlackChess getInstance() {return ClassHolder.instance;}}public class WhiteChess extends AbstractChess{@Overridepublic String getColor() {return "白棋";}private WhiteChess() {}private static class ClassHolder {private static final WhiteChess instance = new WhiteChess();}public static WhiteChess getInstance() {return ClassHolder.instance;}}public class Client {// 存储外部状态(棋子的位置)private static final List<Coordinate> whiteCoordinateList = new ArrayList<>();private static final List<Coordinate> blackCoordinateList = new ArrayList<>();static {whiteCoordinateList.add(new Coordinate(1,4));whiteCoordinateList.add(new Coordinate(2,3));whiteCoordinateList.add(new Coordinate(3,9));blackCoordinateList.add(new Coordinate(2,5));blackCoordinateList.add(new Coordinate(6,9));blackCoordinateList.add(new Coordinate(7,1));}public static void main(String[] args) {for (int i = 0; i < blackCoordinateList.size() && i < whiteCoordinateList.size(); i++) {// 无论下了多少步,黑白棋子都只各创建了一个实例WhiteChess.getInstance().display(whiteCoordinateList.get(i));BlackChess.getInstance().display(blackCoordinateList.get(i));}
// 运行结果:
// 白棋落在{x=1, y=4}
// 黑棋落在{x=2, y=5}
// 白棋落在{x=2, y=3}
// 黑棋落在{x=6, y=9}
// 白棋落在{x=3, y=9}
// 黑棋落在{x=7, y=1}}}
1.1 单纯享元模式
在单纯享元模式中,所有的具体享元类都是可以共享的,不存在非共享具体享元类。
1.2 复合享元模式
将一些单纯享元对象使用组合模式加以组合,形成复合享元对象。这样的复合对象本身不能共享,但是它们包括的单纯享元对象可以被共享。
图 复合享元模式结构图
复合享元模式可以确保复合享元类CompositeConcreteFlyweight中所包含的每个单纯享元类都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。
现实中,当男女双方结婚组合成一个家庭后,虽然双方在有些观念上有分歧,但是夫妻双方二人都会一起为这个家付出的。
public abstract class AbstractRole {public abstract String getRole();void forHome(String things) {System.out.println(getRole() + ":" + things );}}public class HusbandRole extends AbstractRole{@Overridepublic String getRole() {return "丈夫";}private HusbandRole() {}public static class ClassHolder {private final static HusbandRole instance = new HusbandRole();}public static HusbandRole getInstance() {return ClassHolder.instance;}}public class WifeRole extends AbstractRole{@Overridepublic String getRole() {return "妻子";}private WifeRole() {}private static class ClassHolder {private final static WifeRole instance = new WifeRole();}public static WifeRole getInstance() {return ClassHolder.instance;}}public class CompositeManAndWife extends AbstractRole{private final WifeRole wifeRole = WifeRole.getInstance();private final HusbandRole husbandRole = HusbandRole.getInstance();@Overridepublic String getRole() {return wifeRole.getRole() + "和" + husbandRole.getRole();}private CompositeManAndWife() {}private static class ClassHolder {private final static CompositeManAndWife instance = new CompositeManAndWife();}public static CompositeManAndWife getInstance() {return ClassHolder.instance;}}public class Client {public static void main(String[] args) {CompositeManAndWife.getInstance().forHome("赚奶粉钱");CompositeManAndWife.getInstance().forHome("孝敬父母");CompositeManAndWife.getInstance().forHome("去旅行");
// 运行结果:
// 妻子和丈夫:赚奶粉钱
// 妻子和丈夫:孝敬父母
// 妻子和丈夫:去旅行}
}
2 Java的String
public class StringTest {public static void main(String[] args) {String str1 = "hello 享元模式";String str2 = "hello 享元模式";String str3 = new String("hello 享元模式");String str4 = "hello " + "享元模式";String str5 = "hello ";str5 += "享元模式";System.out.println(str1 == str2); //trueSystem.out.println(str1 == str3); //falseSystem.out.println(str1 == str4); //trueSystem.out.println(str1 == str5); //false}}
JVM 开辟了一块存储区专门存储字符串常量,叫作字符串常量池(享元池),而不同的字符串则为不同的享元实体。当给String变量赋值字符串常量时,会在字符串常量池中创建这个字符串享元实体(如果不存在的话)。
而通过new String(“”)的方式创建时则另开辟一个内存空间,而不会直接从字符串常量池中取。最后一个判断为false,是因为str5 的初始字符串和比较的字符串不一致,然后str5通过”+”号连接字符串时,会创建新的字符串变量。
3 享元模式优缺点
优点:
- 客户极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份。
- 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同环境中被共享。
缺点:
- 需要分离出内部状态和外部状态,使系统变得复杂。
- 为了使对象可以共享,将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
4 适用场景
- 系统中有大量相同或者相似的对象。
- 对象中大部分状态都可以外部化。
- 需要多次使用同一享元对象。
相关文章:

享元模式-提供统一实现对象的复用
下围棋时,分为黑白棋子。棋子都一样,这是出现的位置不同而已。如果将每个棋子都作为一个独立的对象存储在内存中,将导致内存空间消耗较大。我们可以将其中不变的部分抽取出来,只存储它的位置信息来实现节约内存。 图 围棋 1 享元模…...

Jenkins工具系列 —— 在Ubuntu 18.04上安装各种版本OpenJDK
文章目录 安装java方式一、使用apt-get工具安装方式二、手动安装java 卸载java各种版本OpenJDK安装包下载 安装java 方式一、使用apt-get工具安装 1、安装各种JAVA版本 若要安装新版本的java11,安装命令: sudo apt install default-jre若选择安装jav…...

vue基础-虚拟dom
vue基础-虚拟dom 1、真实dom目标2、虚拟dom目标 1、真实dom目标 在真实的document对象上,渲染到浏览器上显示的标签。 2、虚拟dom目标 本质是保存节点信息、属性和内容的一个JS对象 更新会监听变化的部分 给真实的DOM打补丁...

C#时间轴曲线图形编辑器开发2-核心功能实现
目录 三、关键帧编辑 1、新建Winform工程 (1)界面布局 (2)全局变量 2、关键帧添加和删除 (1)鼠标在曲线上识别 (2)键盘按键按下捕捉 (3)关键帧添加、删…...

【Git】初始化仓库配置与本地仓库提交流程
目录 一、仓库配置邮箱与用户名 二、本地仓库提交流程 一、仓库配置邮箱与用户名 【Git】Linux服务器Centos环境下安装Git与创建本地仓库_centos git仓库搭建_1373i的博客-CSDN博客https://blog.csdn.net/qq_61903414/article/details/131260033?spm1001.2014.3001.5501 在…...

学习day53
今天主要是做一个案例 TodoList 组件化编码流程: 1. 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突 2.实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:…...
【最短路算法】SPFA
引入 在计算机科学的世界里,算法就像是星空中的繁星,各自闪烁着智慧的光芒。它们沉默而坚定,像是一群不语的哲人,默默地解答着世界的问题。 算法的步骤,如同优美的诗行,让复杂的问题在流转的字符中得以释…...

牛客网Verilog刷题——VL48
牛客网Verilog刷题——VL48 题目答案 题目 在data_en为高期间,data_in将保持不变,data_en为高至少保持3个B时钟周期。表明,当data_en为高时,可将数据进行同步。本题中data_in端数据变化频率很低,相邻两个数据间的变化&…...

Unity UGUI的Shadow(阴影)组件的介绍及使用
Unity UGUI的Shadow(阴影)组件的介绍及使用 1. 什么是Shadow(阴影)组件? Shadow(阴影)组件是Unity UGUI中的一个特效组件,用于在UI元素上添加阴影效果。通过调整阴影的颜色、偏移、模糊等属性,可以使UI元素看起来更加立体和有层次感。 2. …...

Kubernetes系列
文章目录 1 详解docker,踏入容器大门1.1 引言1.2 初始docker1.3 docker安装1.4 docker 卸载1.5 docker 核心概念和底层原理1.5.1 核心概念1.5.2 docker底层原理 1.6 细说docker镜像1.6.1 镜像的常用命令 1.7 docker 容器1.8 docker 容器数据卷1.8.1 直接命令添加1.8.2 Dockerfi…...

同步锁: synchronized
synchronized 1. synchronized的特性2. synchronized的使用3. synchronized的锁机制 1. synchronized的特性 原子性: 所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。可见性: 可见性是指多个线程…...

【微服务】springboot 多模块打包使用详解
目录 一、前言 1.1 为什么需要掌握多模块打包 二、工程模块概述 2.1 前后端不分离...

嵌入式工程师面试经常遇到的30个经典问题
很多同学说很害怕面试,看见面试官会露怯,怕自己的知识体系不完整,怕面试官考的问题回答不上了,所以今天为大家准备了嵌入式工程师面试经常遇到的30个经典问题,希望可以帮助大家提前准备,不再惧怕面试。 1&a…...

ER系列路由器多网段划分设置指南
ER系列路由器多网段划分设置指南 - TP-LINK 服务支持 TP-LINK ER系列路由器支持划分多网段,可以针对不同的LAN接口划分网段,即每一个或多个LAN接口对应一个网段;也可以通过一个LAN接口与支持划分802.1Q VLAN的交换机进行对接,实现…...

3 PostGIS基础查询
PostGIS 基础查询 数据库维护 ps aux | grep postgrespsql 使用命令登录数据库psql -U postgres -d testdb -h localhost -p 5432postgres用户名,testdb数据库名称,localhost ip地址,可以省略,5432端口,可以省略。 …...

Shell错误:/bin/bash^M: bad interpreter: No such file or directory
目录 错误原因和现象 解决方案 错误原因和现象 在执行shell脚本的时候,报错:/bin/bash^M: bad interpreter: No such file or directory。 是由于该脚本文件是在Windows平台编写,然后在MacOS平台中执行。 在Windows平台上文件是dos格式&…...

Golang之路---01 Golang的安装与配置
Golang之路—01 Golang语言安装与配置 官网上下载Windows环境下的安装包 官网下载地址 双击下载后的文件进行安装,可根据需要自定义选择解压后的文件位置。 接着新创建一个文件夹,保存Golang语言项目。 在里面新建bin,pkg,src三个文件夹。 环境变量…...

Anolis OS 8.8服务器采用docker容器方式搭建gerrit3.8.1服务
采用docker容器方式搭建gerrit3.8.1服务 一、选择管理帐户密码的方式二、部署gerrit服务1. 采用docker compose部署单服务的方式部分gerrit(1) docker-compose.yaml文件内容(2) 在docker-compose.yaml文件所在目录调用下面命令先进行初始化操作 2. 在宿主机上部署httpd服务用于…...

PyTorch 中的多 GPU 训练和梯度累积作为替代方案
动动发财的小手,点个赞吧! 在本文[1]中,我们将首先了解数据并行(DP)和分布式数据并行(DDP)算法之间的差异,然后我们将解释什么是梯度累积(GA),最后…...

Appium+python自动化(三十五)- 命令启动appium之 appium服务命令行参数(超详解)
简介 前边介绍的都是通过按钮点击启动按钮来启动appium服务,有的小伙伴或者童鞋们乍一听可能不信,或者会问如何通过命令行启动appium服务呢?且听一一道来。 一睹为快 其实相当的简单,不看不知道,一看吓一跳…...

vmware的window中安装GNS3
1.向vmware中的windows虚拟机传送文件 点击虚拟机-安装VMwaretools 安装在虚拟机上面 此图标代表已经成功,将文件复制到虚拟机上里面 2.安装 安装gns3,需要先安装winpcap(检查网卡)和wireshark(对winpcap上数据进行抓…...

FPGA XDMA 中断模式实现 PCIE3.0 AD7606采集 提供2套工程源码和QT上位机源码
目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案AD7606数据采集和缓存XDMA简介XDMA中断模式QT上位机及其源码 5、vivado工程1--BRAM缓存6、vivado工程2--DDR4缓存7、上板调试验证8、福利:工程代码的获取 1、前言 PCIE(PCI Express&am…...

某某大学某学院后台Phar反序列化GetShell
觉得这个洞还算有点意思,可以记录一下 首先在另一个二级学院进行目录扫描时发现源码www.rar,并且通过一些页面测试推测这两个二级学院应该是使用了同一套CMS 分析源码,发现使用的是ThinkPHP 5.1.34 LTS框架 通过APP、Public得到后台访问路径…...

【ChatGPT辅助学Rust | 基础系列 | 基础语法】变量,数据类型,运算符,控制流
文章目录 简介:一,变量1,变量的定义2,变量的可变性3,变量的隐藏 二、数据类型1,标量类型2,复合类型 三,运算符1,算术运算符2,比较运算符3,逻辑运算…...

使用云服务器和Frp(快速反向代理)框架快速部署实现内网穿透
目录 一. 背景1.1 内网穿透1.2 Frp介绍1.3 Frp配置流程 二. 云服务器配置2.1 配置安全组2.2 编写frps.ini 三. 内网主机配置3.1 编辑frpc.ini文件3.2 启动服务并配置开机自启动 四. 参考文献 一. 背景 现在有一台ubuntu云服务器,我想通过内网穿透将一台内网的主机当…...

Mac 上使用 Tesseract OCR 识别图片文本
Tesseract OCR 引擎:Tesseract是一个开源的OCR引擎,你需要先安装它。可以从Tesseract官方网站(https://github.com/tesseract-ocr/tesseract)下载适用于你的操作系统的安装程序或源代码,并按照官方文档进行安装。 Tes…...

《MapboxGL 基础知识点》- 放大/缩小/定位/级别
中心点 getCenter:获取中心点 const {lng, lat} map.getCenter(); setCenter:设置中心点 // lng, lat map.setCenter([134, 28]); 缩放级别 getZoom:获取当前缩放级别 map.getZoom(); setZoom:设置缩放级别 map.setZoom(5…...

VScode的简单使用
一、VScode的安装 Visual Studio Code简称VS Code,是一款跨平台的、免费且开源的现代轻量级代码编辑器,支持几乎主流开发语言的语法高亮、智能代码补全、自定义快捷键、括号匹配和颜色区分、代码片段提示、代码对比等特性,也拥有对git的开箱…...

# Unity 如何获取Texture 的内存大小
Unity 如何获取Texture 的内存大小 在Unity中,要获取Texture的内存文件大小,可以使用UnityEditor.TextureUtil类中的一些函数。这些函数提供了获取存储内存大小和运行时内存大小的方法。由于UnityEditor.TextureUtil是一个内部类,我们需要使…...

dolphinscheduler switch+传参无坑版
dolphinscheduler 的前后传参有较多的坑,即便是3.0.5版本仍然有一些bug 下面是目前能无坑在3.0.5版本上使用的操作 前置任务 在界面上设置变量和参数名称 跟官方网站不一样,注意最后一行一定使用echo ${setValue(key$query)}的方式,注意引…...