享元模式 (Flyweight Pattern)

定义:

享元模式(Flyweight Pattern)是一种结构型设计模式,用于优化性能和内存使用。它通过共享尽可能多的相似对象来减少内存占用,特别是在有大量对象时。这种模式通常用于减少应用程序中对象的数量,并在多个对象间共享尽可能多的状态。

享元模式的关键是区分内在状态(Intrinsic State)和外在状态(Extrinsic State):

  1. 内在状态(Intrinsic State)
    • 这部分状态是对象共享的。享元对象的内在状态不依赖于享元对象的上下文,即它们是不变的。这使得一个享元对象可以在不同的上下文中共享。
  2. 外在状态(Extrinsic State)
    • 这部分状态则依赖于具体的上下文,并且不能在享元对象之间共享。每个对象实例将有自己独特的外在状态。

享元模式通常涉及以下几个角色:

  • 享元接口(Flyweight):定义了享元对象的新操作,它通常接收并作用于外在状态。
  • 具体享元(Concrete Flyweight):实现享元接口,并存储内在状态。具体享元对象必须是可共享的,它的状态不应当随上下文变化。
  • 享元工厂(Flyweight Factory):负责创建和管理享元对象,确保合理地共享享元。

享元模式适用于程序中存在大量相似对象的情况,可以有效地减少内存占用,提高性能。典型的应用场景包括字符串常量池、数据库连接池、缓存等。

解决的问题:
  • 减少内存占用
    • 当系统中存在大量相似或相同的对象时,这些对象占用大量内存。享元模式通过共享相似对象来减少内存消耗。
  • 提高性能
    • 减少对象创建的数量意味着降低了内存占用和系统垃圾回收的负担,从而提高应用性能。
  • 管理共享对象
    • 在享元模式中,共享对象的管理变得更加集中和统一,使得对共享对象的维护和更新更加高效。
  • 优化数据结构
    • 在处理大量小粒度对象的场景中,享元模式帮助减少应用程序中的对象数量,从而优化数据结构和提高运行效率。
  • 减少系统复杂性
    • 通过减少对象实例的数量,系统变得更加简单,易于理解和维护。

享元模式通过区分内在状态和外在状态,并共享内在状态,解决了大量对象存在时的性能和内存问题。这种模式在诸如文本处理、图形渲染、数据库连接池等需要大量小粒度对象的场景中尤为有用。

使用场景:
  • 大量相似对象的场景
    • 当应用程序需要创建大量相似的对象,并且这些对象的大部分状态可以被共享时,使用享元模式可以显著减少内存占用。这种情况常见于处理大量小粒度对象的系统。
  • 内存限制严格的应用
    • 在资源有限或内存受限的应用程序(如移动设备或嵌入式系统)中,享元模式能有效减少内存消耗。
  • 重复对象的数据库存储
    • 当相同的数据需要在数据库中多次存储时,可以使用享元模式来共享这些数据,减少数据库的存储负担。
  • 图形相关应用
    • 在图形相关的应用程序中,例如图形编辑器或游戏,常常需要创建大量相似的图形对象,如图标、线条、字符等。享元模式能够减少这些对象的数量,优化性能和资源使用。
  • 字符串池
    • 在程序中,字符串常量池是享元模式的一个典型应用。例如,Java中的String对象实现采用享元模式来避免重复创建相同的字符串字面量。
  • 对象缓存
    • 当对象的创建和销毁成本较高时,可以将这些对象缓存起来供后续重用,这是享元模式的另一种应用场景。
  • 系统状态共享
    • 在需要在多个对象间共享某些状态(如配置信息、环境设置等)时,享元模式可以避免重复存储这些状态。

享元模式的关键在于识别哪些状态是可以共享的(内在状态),哪些状态是依赖于上下文的(外在状态),并且只共享内在状态。正确应用享元模式可以带来显著的性能提升和内存使用优化。

示例代码 1 - 简单享元模式:
// 享元接口
public interface Flyweight {void operation(String extrinsicState);
}// 具体享元类
class ConcreteFlyweight implements Flyweight {private String intrinsicState;public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void operation(String extrinsicState) {System.out.println("Intrinsic State = " + intrinsicState +", Extrinsic State = " + extrinsicState);}
}// 享元工厂
class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {if (!flyweights.containsKey(key)) {flyweights.put(key, new ConcreteFlyweight(key));}return flyweights.get(key);}
}// 客户端代码展示了如何使用享元模式
public class FlyweightPatternDemo {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("Key1");flyweight1.doOperation("Operation1");Flyweight flyweight2 = factory.getFlyweight("Key2");flyweight2.doOperation("Operation2");Flyweight flyweight3 = factory.getFlyweight("Key1");flyweight3.doOperation("Operation3");}
}

在此示例中,FlyweightFactory 管理着享元对象的集合。当客户端请求一个享元时,工厂首先检查是否已经创建了具有相应内在状态的享元对象。如果是,工厂返回现有的对象;如果不是,工厂将创建一个新的享元对象。这种方式确保具有相同内在状态的享元对象在系统中只有一个实例,从而实现对象的共享。

此示例中,ConcreteFlyweight 类的实例是根据内在状态(intrinsicState)共享的。客户端通过传递外在状态(extrinsicState)给享元对象的方法,实现了对状态的操作。这种分离内在状态和外在状态的方法是享元模式的核心,它允许系统有效地共享对象,减少内存占用,提高性能。

示例代码 2 - 享元模式在文本格式化中的应用:
// 字符享元
class CharacterFlyweight {private final char intrinsicChar;public CharacterFlyweight(char intrinsicChar) {this.intrinsicChar = intrinsicChar;}public void display(int fontSize) {System.out.println("Character: " + intrinsicChar + ", Font size: " + fontSize);}
}// 享元工厂
class CharacterFactory {private final Map<Character, CharacterFlyweight> pool = new HashMap<>();public CharacterFlyweight getCharacter(char ch) {CharacterFlyweight flyweight = pool.get(ch);if (flyweight == null) {flyweight = new CharacterFlyweight(ch);pool.put(ch, flyweight);}return flyweight;}
}
主要符合的设计原则:
  1. 开闭原则(Open-Closed Principle):
    • 享元模式支持在不修改现有代码的情况下,增加新的享元实现。可以扩展享元工厂以支持新类型的享元对象,而无需修改现有的享元类和客户端代码。
  2. 单一职责原则(Single Responsibility Principle):
    • 享元模式中的享元对象专注于实现内在状态的行为,而享元工厂则专注于管理和创建享元对象。这样的分工使每个类只有一个原因引起变化,符合单一职责原则。
  3. 最少知识原则(Principle of Least Knowledge)/迪米特法则(Law of Demeter):
    • 享元模式鼓励使用最少的知识去完成任务,客户端不需要知道享元对象的内部结构和存储方式,只需通过享元工厂来获取所需的享元对象。

享元模式通过有效地共享对象来优化内存和性能,特别适用于大量类似对象频繁创建和销毁的场景。通过区分内在状态和外在状态,享元模式确保共享对象的高效管理。

在JDK中的应用:
  • 字符串常量池(String Constant Pool):
    • 在Java中,字符串常量池是享元模式的一个典型例子。相同的字符串字面量只存储一次。当创建相同内容的字符串时,Java会首先检查字符串常量池中是否存在该字符串,如果存在,则返回对池中字符串的引用,而不是创建一个新的对象。
  • 包装类型的值缓存:
    • Java的自动装箱过程使用享元模式来缓存一定范围内的包装对象。例如,Integer.valueOf() 方法会缓存 -128127 之间的 Integer 对象。当在这个范围内创建Integer对象时,会直接返回缓存中的对象而不是每次都创建新对象。
  • java.awt.Font:
    • AWT库中的 Font 类使用享元模式来减少创建字体实例的内存开销。由于字体创建代价较大,共享相同属性的字体实例可以显著提高性能和减少内存消耗。

这些应用展示了享元模式在Java标准库中的普遍性和实用性。特别是在处理大量细粒度对象时,通过共享对象来优化内存和性能,从而提升应用的效率和响应速度。

在Spring中的应用:
  • Spring Bean的作用域
    • 在Spring框架中,单例模式是Bean默认的作用域。虽然这是单例模式的应用,但它也体现了享元模式的核心思想 —— 对象共享。单例Bean在Spring容器中只创建一次,之后每次请求都使用相同的实例,从而减少了对象创建的开销。
  • Spring Security的上下文持有者
    • Spring Security中使用的SecurityContextHolder策略,可以看作是享元模式的一个应用。它为不同的安全上下文提供统一的访问点,并在内部通过线程局部变量共享安全上下文信息。
  • 缓存抽象
    • Spring的缓存抽象允许在应用中轻松地添加和管理缓存,这也是一种享元模式的体现。缓存的目的是重用之前计算的结果,减少资源密集型操作的重复执行。
  • 数据访问对象(DAO)
    • 在Spring框架中,DAO类通常被设计为无状态的单例Bean,这意味着它们在应用中被共享。虽然这本质上是单例模式的应用,但是它也体现了享元模式的思想,即共享对象以减少内存占用和提高性能。

尽管Spring框架中享元模式的应用可能不如其他设计模式那样显著,但在处理对象共享和重用方面,享元模式的思想仍然在Spring的设计和实现中发挥着作用。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183304.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis 实战缓存

本篇概要&#xff1a; 1. 设置、查询、获取过期时间&#xff1b;2. 缓存穿透&#xff1a;设置空键&#xff1b;3. 封杀单ip&#xff1b;4. 封杀ip段&#xff1b;5. 缓存预热&#xff1b;6. 使用 hash 数据类型保存新闻的缓存&#xff0c;增加点击量&#xff1b;7. Sorted set&a…

纯js实现录屏并保存视频到本地的尝试

前言&#xff1a;先了解下&#xff1a;navigator.mediaDevices&#xff0c;mediaDevices 是 Navigator 只读属性&#xff0c;返回一个 MediaDevices 对象&#xff0c;该对象可提供对相机和麦克风等媒体输入设备的连接访问&#xff0c;也包括屏幕共享。 const media navigator…

【刷题】DFS

DFS 递归&#xff1a; 1.判断是否失败终止 2.判断是否成功终止&#xff0c;如果成功的&#xff0c;记录一个成果 3.遍历各种选择&#xff0c;在这部分可以进行剪枝 4.在每种情况下进行DFS&#xff0c;并进行回退。 199. 二叉树的右视图 给定一个二叉树的 根节点 root&#x…

深度学习之十二(图像翻译AI算法--UNIT(Unified Neural Translation))

概念 UNIT(Unified Neural Translation)是一种用于图像翻译的 AI 模型。它是一种基于生成对抗网络(GAN)的框架,用于将图像从一个域转换到另一个域。在图像翻译中,这意味着将一个风格或内容的图像转换为另一个风格或内容的图像,而不改变图像的内容或语义。 UNIT 的核心…

IDEA2022 Git 回滚及回滚内容恢复

IDEA2022 Git 回滚 ①选择要回滚的地方&#xff0c;右键选择 ②选择要回滚的模式 ③开始回滚 ④soft模式回滚的内容会保留在暂存区 ⑤输入git push -f &#xff0c;然后重新提交&#xff0c;即可同步远程 注意观察IDEA右下角分支的标记&#xff0c;蓝色代表远程内容未同步到本…

数据结构 / day06 作业

1.下面的代码打印在屏幕上的值是多少? /下面的代码打印在屏幕上的值是多少?#include "stdio.h"int compute_data(int arr[], unsigned int len) {long long int result 0;if(result len)return arr[0];resultcompute_data(arr,--len);printf("len%d, res…

elementui中table进行表单验证

<el-form :model"ruleForm" ref"ruleForm" class"demo-ruleForm"><el-table :data"ruleForm.tableDataShou" border style"width: 100%;"><el-table-column type"index" label"序号" wi…

Android12源码分析

Android 12的源码结构与之前的版本类似&#xff0c;但也有一些新的变化和特性。以下是对Android 12源码结构的简要解析&#xff1a; 1. 系统源代码&#xff1a;这部分包含了整个Android操作系统的核心代码&#xff0c;包括Linux内核、系统库、运行时环境&#xff08;ART&#…

Flink源码解析零之重要名词的理解

名词解释 1)StreamGraph 根据用户通过 Stream API 编写的代码生成的最初的图。 (1)StreamNode 用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。 (2)StreamEdge 表示连接两个StreamNode的边。 2)JobGraph StreamGraph经过优化后生成了 J…

【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能

文章目录 ⭐⭐⭐Spring核心源码分析自定义Spring框架⭐⭐⭐一、Spring使用回顾二、Spring核心功能结构1、Spring核心功能2、bean概述 三、Spring IOC相关接口分析1、BeanFactory解析2、BeanDefinition解析3、BeanDefinitionReader解析4、BeanDefinitionRegistry解析5、创建容器…

伪集群配置

编辑core-site 配置core-site 配置hdfs-site 将以下的文件配置进去 启动一下hadoop产生tmp文件 产生这个叫namenode的文件并格式化 回到~目录 再配置以下信息 配置以下信息 重启文件 再重新格式化配置namenode 再启动一下&#xff0c;然后jps看看&#xff0c;出现这样就…

java后端自学总结

自学总结 MessageSource国际化接口总结 MessageSource国际化接口 今天第一次使用MessageSource接口,比较意外遇到了一些坑 messageSource是spring中的转换消息接口&#xff0c;提供了国际化信息的能力。MessageSource用于解析 消息&#xff0c;并支持消息的参数化和国际化。 S…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

机器学习中参数优化或交叉验证评估指标含义

在Scikit-Learn中&#xff0c;cross_val_score函数支持多种不同的评分标准&#xff08;scoring参数&#xff09;。以下是一些常见的评分标准及其应用场景&#xff1a; 参考链接&#xff1a; https://blog.csdn.net/worther/article/details/126909270 https://zhuanlan.zhihu.c…

优化器原理——权重衰减(weight_decay)

优化器原理——权重衰减&#xff08;weight_decay&#xff09; weight_decay的作用 原理解析 实验观察 在深度学习中&#xff0c;优化器的 weight_decay 参数扮演着至关重要的角色。它主要用于实现正则化&#xff0c;以防止模型过拟合。过拟合是指模型在训练数据上表现优异&…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷12

单选题 1、电子邮件地址中一定会出现的字符&#xff08;&#xff09; A、- B、 C、&#xff01; D、# 答案&#xff1a;B 2、以下常见的电脑硬件中&#xff0c;&#xff08;&#xff09;不是一种输入设备 A、鼠标 B、键盘 C、触摸板 D、显示器 答案&#xff1a;D 3、…

python | 简易版: pdf 转换为 word 方法

一、 前言 本文利用python将pdf转换为word方法&#xff0c;需要用到两个第三方模块&#xff0c;分别是&#xff1a; &#xff08;1&#xff09;pdfplumber&#xff0c;用来解析pdf文档&#xff0c;包括pdf的基本信息&#xff08;作者、创建时间、修改时间…&#xff09;及表格…

从意义中恢复,而不是从数据包中恢复

从书报&#xff0c;录放机&#xff0c;电视机到智能手机&#xff0c;vr 眼镜&#xff0c;所有学习的&#xff0c;娱乐的工具或玩具&#xff0c;几乎都以光声诉诸视听&#xff0c;一块屏幕和一个喇叭。 视觉和听觉对任何动物都是收发信息的核心&#xff0c;诉诸视觉和听觉的光和…

批量将本地N个英文Html文档进行中文翻译-源码篇

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

机器学习笔记 - 3D数据的常见表示方式

一、简述 从单一角度而自动合成3D数据是人类视觉和大脑的基本功能,这对计算机视觉算法来说是比较难的。但随着LiDAR、RGB-D 相机(RealSense、Kinect)和3D扫描仪等3D传感器的普及和价格的降低,3D 采集技术的最新进展取得了巨大飞跃。与广泛使用的 2D 数据不同,3D 数据具有丰…