享元模式-提供统一实现对象的复用
下围棋时,分为黑白棋子。棋子都一样,这是出现的位置不同而已。如果将每个棋子都作为一个独立的对象存储在内存中,将导致内存空间消耗较大。我们可以将其中不变的部分抽取出来,只存储它的位置信息来实现节约内存。
图 围棋
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服务呢?且听一一道来。 一睹为快 其实相当的简单,不看不知道,一看吓一跳…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...

VisualXML全新升级 | 新增数据库编辑功能
VisualXML是一个功能强大的网络总线设计工具,专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑(如DBC、LDF、ARXML、HEX等),并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...