Java二十三种设计模式-享元模式(12/23)

享元模式:高效管理大量对象的设计模式

引言

在软件开发中,有时需要处理大量相似或重复的对象,这可能导致内存使用效率低下和性能问题。享元模式提供了一种解决方案,通过共享对象的共同部分来减少内存占用。

基础知识,java设计模式总体来说设计模式分为三大类:

(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

第一部分:享元模式概述

1.1 定义与特点

享元模式的基本定义

享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。

享元模式是一种结构型设计模式,旨在通过共享来减少创建大量相似或相同对象时的内存消耗。这种模式通过共享对象的共有部分来实现对象的复用,从而降低系统资源的使用。

特点

  • 共享不变部分:享元模式的核心在于识别对象的共有特性,并在多个对象之间共享这些特性,而不是为每个对象独立创建。
  • 减少对象数量:通过共享对象,减少了系统中对象的总数量,降低了内存占用和垃圾回收的负担。
  • 细粒度对象:特别适用于大量细粒度对象的场景,例如字体渲染、图形用户界面中的图标等。

1.2 应用场景

何时适合使用享元模式

  • 大量相似对象:当系统中存在大量相似或相同的对象时,使用享元模式可以显著减少内存消耗。
  • 对象创建成本高:如果对象的创建过程复杂或成本高昂,享元模式可以通过共享来降低这些成本。
  • 对象状态外部化:当对象的状态可以被外部化,即对象的行为可以通过外部状态参数化,而不是依赖于对象内部状态时。

应用实例

  • 文本编辑器:在文本编辑器中,字符可以作为享元,因为文档中可能存在大量重复的字符。
  • 图形界面:在GUI设计中,按钮、图标等界面元素可能在多个地方重复使用,通过享元模式可以减少这些元素的内存占用。
  • 游戏开发:游戏中的敌人、树木、建筑等元素可能在多个场景中重复出现,享元模式可以有效地管理这些资源。

享元模式通过共享对象的共有部分来减少内存占用,提高资源使用效率。然而,这种模式也增加了系统的复杂性,需要仔细设计以确保对象的共享部分不会影响其独立性。在下一部分中,我们将详细介绍享元模式的组成部分和实现方式。

第二部分:享元模式的组成

2.1 角色定义

享元(Flyweight)

  • 定义:享元是实现共享的对象,它包含了可以被多个对象共享的内部状态。
  • 特点:享元对象通常很轻量,不包含任何唯一性的状态。

享元工厂(Flyweight Factory)

  • 定义:享元工厂负责创建和管理享元对象,确保享元对象可以被正确地共享和复用。
  • 特点:享元工厂维护一个享元对象池,用于存储和检索享元对象。

客户端(Client)

  • 定义:客户端是使用享元对象的代码,它通过享元工厂来请求所需的享元对象。
  • 特点:客户端不直接创建享元对象,而是通过享元工厂来获取。

2.2 职责分配

享元的职责

  • 维护内部状态:享元对象负责维护可以被共享的状态,如不变的数据或行为。
  • 接受外部状态:享元对象可以接受外部状态,这些状态通常在享元对象被使用时传递。

享元工厂的职责

  • 创建享元对象:享元工厂负责创建享元对象,确保不会创建重复的对象。
  • 管理对象池:享元工厂维护一个对象池,存储已经创建的享元对象,以便复用。
  • 分配享元对象:享元工厂负责根据客户端的请求分配享元对象,确保对象的正确共享。

客户端的职责

  • 请求享元对象:客户端通过享元工厂请求所需的享元对象。
  • 传递外部状态:客户端负责传递享元对象所需的外部状态,以便享元对象可以正确执行操作。
  • 使用享元对象:客户端使用从享元工厂获取的享元对象来执行所需的任务。

通过这种职责分配,享元模式能够有效地管理大量对象,减少内存消耗,并提高系统性能。在下一部分中,我们将通过Java代码示例来展示享元模式的具体实现。

第三部分:享元模式的实现

3.1 Java实现示例

以下是使用Java语言实现享元模式的代码示例。在这个例子中,我们将创建一个简单的文本编辑器,其中字符是享元对象。

// 享元接口
interface Flyweight {void print(String extrinsicState);
}// 具体享元类
class ConcreteFlyweight implements Flyweight {private String intrinsicState;public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void print(String extrinsicState) {System.out.println("Character: " + intrinsicState + " with intrinsic state: " + this.intrinsicState);}
}// 享元工厂
class FlyweightFactory {private HashMap<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 Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight characterA = factory.getFlyweight("A");characterA.print("Bold");Flyweight characterB = factory.getFlyweight("B");characterB.print("Italic");}
}

3.2 设计考虑

享元对象的共享性

  • 内部状态:享元对象的内部状态应该是共享的,并且不随环境改变而改变。
  • 外部状态:享元对象的外部状态应该由客户端提供,并且可以随环境变化。

享元工厂的设计

  • 对象池管理:享元工厂需要管理一个对象池,以存储和复用享元对象。
  • 线程安全:在多线程环境中,享元工厂需要确保线程安全。

享元对象的创建

  • 延迟初始化:享元对象的创建可以延迟到第一次使用时,以节省资源。
  • 对象复用:确保享元对象在不再使用时可以被回收并复用于其他客户端。

享元模式的灵活性

  • 扩展性:享元模式应允许新类型的享元对象容易地添加到系统中。
  • 兼容性:新添加的享元对象应与现有客户端代码兼容。

避免过度共享

  • 识别共享部分:正确识别哪些部分可以共享,哪些部分应该是唯一的。
  • 性能权衡:过度共享可能导致性能问题,如同步开销。

享元模式的适用性

  • 对象数量:评估系统中对象的数量,确定是否值得使用享元模式。
  • 对象大小:如果对象较小,享元模式可能不会带来显著的性能提升。

通过考虑这些设计问题,你可以更有效地实现享元模式,确保它为你的应用程序带来内存和性能上的好处。在下一部分中,我们将探讨享元模式的使用场景、优点与缺点。

第四部分:享元模式的使用场景

4.1 内存敏感的应用

在内存敏感的应用中,享元模式发挥着至关重要的作用,尤其是在资源受限的环境中。

讨论在内存敏感的应用中享元模式的应用:

  • 移动设备:在移动应用开发中,由于内存限制,享元模式可以用来减少内存消耗。
  • 嵌入式系统:在嵌入式系统中,内存和处理能力有限,使用享元模式可以提高效率。
  • 大型数据处理:在处理大规模数据集时,享元模式可以减少创建和销毁对象的开销。

应用实例:

  • 图像处理软件:在图像处理中,像素可以作为享元对象,通过共享减少内存使用。
  • 游戏开发:游戏中的敌人或道具,如果具有相似的行为和外观,可以共享实现。

4.2 大量相似对象的管理

当应用程序需要管理大量相似的对象时,享元模式可以显著提高内存使用效率和性能。

分析在需要管理大量相似对象时,享元模式的优势:

  • 减少内存占用:通过共享相似对象的共有状态,减少了内存占用。
  • 提高性能:减少了对象创建和销毁的时间,提高了程序性能。
  • 简化对象管理:享元工厂提供了一个集中的点来管理对象的创建和复用。

应用实例:

  • 文本编辑器:文本中的字符如果重复出现,可以作为享元对象来处理。
  • 图形界面:GUI组件如按钮、图标等,如果样式和行为相同,可以共享实现。

优势详解:

  • 对象池:享元模式通过对象池来管理对象,避免了频繁的创建和销毁。
  • 细粒度控制:享元模式允许对对象的细粒度控制,可以针对特定状态进行优化。
  • 灵活性:享元模式提供了在不增加额外内存负担的情况下扩展对象功能的能力。

通过这些使用场景的讨论,我们可以看到享元模式在处理大量相似对象时的优势。然而,也要注意享元模式可能会增加系统的复杂性,并且在某些情况下可能不是最佳解决方案。在下一部分中,我们将探讨享元模式的优点与缺点。

第五部分:享元模式的优点与缺点

5.1 优点

减少内存消耗

  • 共享对象:通过共享对象的内部状态,减少了内存中对象的总数量。
  • 对象复用:享元模式允许对象在不同地方被复用,避免了重复创建相同对象。

提高性能

  • 减少创建时间:由于减少了对象的创建,享元模式可以降低系统的响应时间。
  • 快速响应:对象的快速获取和释放可以提高应用程序的交互性能。

降低系统成本

  • 资源优化:在资源受限的环境中,享元模式可以显著降低系统运行成本。

提高代码的可维护性

  • 集中管理:享元工厂提供了对象的集中管理,简化了对象的创建和维护。

易于扩展

  • 添加新对象:新类型的享元对象可以很容易地添加到系统中,而不影响现有代码。

5.2 缺点

增加系统复杂性

  • 设计复杂性:享元模式增加了设计的复杂性,需要仔细规划内部状态和外部状态的分离。

过度共享的风险

  • 共享状态管理:如果共享状态管理不当,可能会导致数据不一致或竞态条件。

灵活性降低

  • 修改困难:一旦享元对象被广泛使用,对其进行修改可能会变得困难。

性能权衡

  • 同步开销:在多线程环境中,共享对象可能需要额外的同步机制,这可能会影响性能。

不适用于所有场景

  • 独特对象:如果对象之间的差异性很大,享元模式可能不是最佳选择。

难以识别享元对象

  • 内部状态识别:有时难以识别哪些部分是共享的内部状态,哪些是外部状态。

可能影响缓存

  • 缓存策略:享元模式可能会影响系统的缓存策略和内存访问模式。

享元模式是一种强大的设计模式,可以显著减少内存消耗并提高性能,特别是在处理大量相似对象的场景中。然而,它也需要谨慎使用,以避免增加系统的复杂性和维护难度。在实际应用中,根据具体需求和场景选择是否使用享元模式是非常重要的。在下一部分中,我们将比较享元模式与其他设计模式,并提供一些最佳实践和建议。

第六部分:享元模式与其他模式的比较

6.1 与单例模式的比较

单例模式

  • 定义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
  • 使用场景:当需要严格控制资源数量,如配置管理器或全局缓存时。

享元模式

  • 定义:享元模式通过共享来高效地支持大量细粒度对象的复用。
  • 使用场景:当需要创建大量相似或可共享的对象时。

对比

  • 对象数量:单例模式限制对象只能有一个实例,而享元模式可以有多个实例,但通过共享来减少数量。
  • 共享机制:单例模式不涉及对象的共享,享元模式则依赖于对象的共享。
  • 目的:单例模式用于控制对象的创建数量,享元模式用于减少对象的内存占用。

6.2 与原型模式的对比

原型模式

  • 定义:原型模式使用原型实例指定创建对象的种类,通过复制这些原型创建新的对象。
  • 使用场景:当创建新对象的成本较高,或者需要快速复制现有对象时。

享元模式

  • 定义:享元模式通过共享不变的内部状态来减少对象的创建。
  • 使用场景:适用于创建大量相似对象,且对象的内部状态可以共享。

对比

  • 复用方式:原型模式通过复制原型来创建新对象,享元模式通过共享已有对象来复用。
  • 共享程度:原型模式通常用于完全独立的复制,享元模式则侧重于共享不变的状态。

第七部分:享元模式的最佳实践和建议

7.1 最佳实践

合理划分内部状态和外部状态

  • 内部状态:应为共享状态,不随对象的使用环境改变而改变。
  • 外部状态:应为瞬时状态,可以随享元对象的使用而变化,不影响其他对象。

确保享元对象的不变性

  • 不变对象:保持享元对象的状态不可变,确保共享的安全性。

使用享元工厂管理对象

  • 对象池:享元工厂应维护一个对象池,以便存储和复用享元对象。

避免外部状态的误用

  • 参数化:确保外部状态通过参数传递,避免错误地修改享元对象的内部状态。

保持享元对象的轻量级

  • 轻量设计:享元对象应设计得尽可能轻量,避免包含大量资源消耗。

考虑线程安全

  • 并发访问:在多线程环境中使用享元模式时,确保享元对象的线程安全。

7.2 避免滥用

避免过度设计

  • 简单问题复杂化:对于不需要共享的对象,避免使用享元模式,以免增加不必要的复杂性。

避免共享不当

  • 错误共享:避免将不应该共享的状态共享,这可能会导致数据不一致。

避免忽视性能影响

  • 性能评估:在引入享元模式之前,评估其对性能的实际影响,确保它确实能带来性能上的提升。

7.3 替代方案

使用对象池

  • 对象复用:对象池是另一种管理对象生命周期的方式,适用于需要频繁创建和销毁对象的场景。

采用原型模式

  • 快速复制:当对象的创建成本较高时,可以使用原型模式来快速复制已有对象。

依赖注入

  • 控制反转:依赖注入可以减少对象创建的复杂性,提高系统的灵活性和可测试性。

状态模式

  • 状态变化:当对象的状态变化复杂时,可以使用状态模式来管理对象的状态转换。

享元模式与组合模式结合

  • 部分-整体层次:在需要表示部分-整体结构时,可以结合使用享元模式和组合模式。

享元模式是一种强大的设计模式,可以在需要高效管理大量对象时减少内存消耗和提高性能。然而,合理使用享元模式并避免其缺点是至关重要的。了解其替代方案可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用享元模式,以达到最佳的设计效果。

结语

享元模式是一种有效的设计模式,用于通过共享来减少大量对象的内存占用。通过本文的深入分析,希望读者能够对享元模式有更全面的理解,并在实际开发中做出合理的设计选择。

博主还写了其他Java设计模式文章,请各位大佬批评指正:

(一)创建型模式(5种):

Java二十三种设计模式-单例模式(1/23)

Java二十三种设计模式-工厂方法模式(2/23)

Java二十三种设计模式-抽象工厂模式(3/23)

Java二十三种设计模式-建造者模式(4/23)

Java二十三种设计模式-原型模式(5/23)

(二)结构型模式(7种): 

Java二十三种设计模式-适配器模式(6/23)

Java二十三种设计模式-装饰器模式(7/23)

Java二十三种设计模式-代理模式(8/23)

Java二十三种设计模式-外观模式(9/23)

Java二十三种设计模式-桥接模式(10/23)

Java二十三种设计模式-组合模式(11/23)

Java二十三种设计模式-享元模式(12/23)

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

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

相关文章

谷粒商城实战笔记-145-性能压测-性能监控-jvisualvm使用-解决插件不能安装

文章目录 jvisualvm的作用安装查看gc相关信息的插件解决jvisualvm不能正常安装插件的问题1&#xff0c;查看java版本2&#xff0c;打开网址3&#xff0c;修改jvisualvm的设置 jvisualvm的作用 JVisualVM是一个集成在Java Development Kit (JDK) 中的多功能工具&#xff0c;它提…

LLMOps — 使用 BentoML 为 Llama-3 模型提供服务

使用 BentoML 和 Runpod 快速设置 LLM API 经常看到数据科学家对 LLM 的开发感兴趣&#xff0c;包括模型架构、训练技术或数据收集。然而&#xff0c;我注意到&#xff0c;很多时候&#xff0c;除了理论方面&#xff0c;许多人在以用户实际使用的方式提供这些模型时遇到了问题…

【C++】—— 类与对象(三)

【C】—— 类与对象&#xff08;三&#xff09; 4、拷贝构造函数4.1、初识拷贝构造4.1.1、为什么要传引用4.1.2、引用尽量加上 const 4.2、深入拷贝构造4.2.1、为什么要自己实现拷贝构造4.2.2、传值返回先调用拷贝构造的原因4.2.3、躺赢的 MyQueue4.2.4、传值返回与引用返回 4.…

世界500强排行榜公布 中国互联网企业表现突出

在2024年8月5日&#xff0c;《财富》杂志公布了最新的全球500强企业排行榜。 这些公司的总营收接近41万亿美元&#xff0c;占到了全球GDP的三分之一&#xff0c;其净利润同比增长2.3%&#xff0c;总计约2.97万亿美元。 中国有133家公司入选这一榜单&#xff0c;以11万亿美元的…

SpringMVC学习笔记---带你快速入门和复习

一、初识SpringMVC 1.1、什么是SpringMVC 1.1.1、什么是MVC MVC是一种软件架构模式&#xff08;是一种软件架构设计思想&#xff0c;不止Java开发中用到&#xff0c;其它语言也需要用到&#xff09;&#xff0c;它将应用分为三块&#xff1a; M&#xff1a;Model&#xff0…

数学建模--蒙特卡洛算法之电子管更换刀片寿命问题

目录 1.电子管问题重述 2.电子管问题分析 3.电子管问题求解 4.刀片问题重述 5.刀片问题分析 6.刀片问题求解 1.电子管问题重述 某设备上安装有4只型号规格完全相同的电子管&#xff0c;已知电子管寿命服从100&#xff5e;200h之间的均匀分布&#xff0e; 只要有一个电子管…

在线办公小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;通知公告管理&#xff0c;员工管理&#xff0c;部门信息管理&#xff0c;职位信息管理&#xff0c;会议记录管理&#xff0c;待办事项管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首…

Android经典实战之如何获取图片的经纬度以及如何根据经纬度获取对应的地点名称

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 在Android中&#xff0c;可以通过以下步骤获取图片的经纬度信息以及根据这些经纬度信息获取对应的地点名称。这里主要涉及两部分&#xff1a;从…

从0开始搭建vue + flask 旅游景点数据分析系统(七):可视化前后端对接实现

这一期继续编写flask后端&#xff0c;并且完成echarts折线图、柱状图和饼图的对接。 1 新增一些依赖 pip install Flask-SQLAlchemy Flask-Marshmallow pymysql修改 init.py文件&#xff0c;下面给出完整代码&#xff1a; from flask import Flask from flask_sqlalchemy im…

leetcode70_爬楼梯

思路 动态规划 爬到第n阶楼梯的方法数为&#xff1a;第n-1阶楼梯的方法数 第n-2阶楼梯的方法数 func climbStairs(n int) int {if n < 2 {return 1}dp : make([]int, n1)dp[1] 1dp[2] 2for i:3; i<n; i {dp[i] dp[i-1] dp[i-2]}return dp[n] }

Kubernetes中的PV)和 PVC深度剖析

在容器化的世界里&#xff0c;持久化存储一直是一个重要且复杂的问题。Kubernetes&#xff08;以下简称K8s&#xff09;为了解决容器中的数据持久化问题&#xff0c;提出了Persistent Volume&#xff08;PV&#xff09;和Persistent Volume Claim&#xff08;PVC&#xff09;这…

大数据信用报告查询哪家平台的比较好?

相信在搜索大数据信用的你&#xff0c;已经因为大数据信用不好受到了挫折&#xff0c;想详细了解一下自己的大数据信用&#xff0c;但是找遍了网络上的平台之后才发现&#xff0c;很多平台都只提供查询服务&#xff0c;想要找一个专业的平台查询和讲解很困难。下面本文就为大家…

LeetCode 150.逆波兰表达式求值

LeetCode 150.逆波兰表达式求值 思路&#x1f9d0;&#xff1a; 用栈存储该字符串&#xff0c;如果遇到数字就入栈&#xff0c;遇到符号就将数字出栈计算后再入栈&#xff0c;当整个字符串遍历完后&#xff0c;栈顶值就是该表达式的值。 代码&#x1f50e;&#xff1a; class …

【OpenCV C++20 学习笔记】范围阈值操作

范围阈值操作 原理HSV颜色空间RGB与HSV颜色空间之间的转换 代码实现颜色空间的转换范围阈值操作 原理 HSV颜色空间 HSV(色相hue, 饱和度sarturation, 色明度value)颜色空间与RGB颜色空间相似。hue色相通道代表颜色类型&#xff1b;saturation饱和度通道代表颜色的饱和度&…

MySQL-MHA高可用配置及故障切换

目录 案例搭建 1&#xff1a;所有服务器关闭防火墙 2&#xff1a;设置hosts文件 3&#xff1a;安装 MySQL 数据库 4&#xff1a;修改参数 5&#xff1a;安装 MHA 软件 6&#xff1a;配置无密码认证 7&#xff1a;配置 MHA 8&#xff1a;模拟 master 故障 MHA(MasterHi…

【Python修改所有可执行程序的图标】

孩子还小&#xff0c;不懂事写着玩的 警告&#xff1a;请谨慎使用。该程序会修改全系统所有可执行文件图标(其实就是注册表)&#xff0c;在重新开机后生效 演示&#xff1a; 把应用程序图标改为记事本&#x1f5d2; 原理&#xff1a; Windows 操作系统通过注册表来存储和管…

不懂期权怎么交易?看这个例子就懂了

期权就是股票&#xff0c;唯一区别标的物上证指数&#xff0c;会看大盘吧&#xff0c;期权交易两个方向认购做多&#xff0c;认沽做空&#xff0c;双向t0交易没了&#xff0c;跟期货一样&#xff0c;对的&#xff0c;玩的也是合约&#xff0c;唯一区别没有保证金不会爆仓&#…

CAD二次开发IFoxCAD框架系列(15)- IFox的介绍和初始化

背景介绍 因为本人在光伏行业从事软件研发&#xff0c;最近我一直在做CAD方面技术的预研和探索。在研究CAD的SDK的时候&#xff0c;发现很多写法比较繁琐&#xff0c;所以一直想封装工具&#xff0c;提高开发效率&#xff0c;也做了很多的CAD工具的封装&#xff0c;大家可以看…

一文读懂如何选择视频孪生三维建模方式及建模精度等级

导言/INTRODUCTION 三维模型是视频孪生应用的基础&#xff0c;建模方式与模型精度将直接影响到最终孪生场景的呈现和应用效果。各种建模方式和模型精度在成本、场景还原真实度、实施周期方面都有自己的特点&#xff0c;因而有着各自的优劣势和适用场景&#xff0c;同一场景可能…

鸿蒙开发5.0【应用异常处理】运维

应用异常处理 介绍 本示例介绍了通过应用事件打点hiAppEvent获取上一次应用异常信息的方法&#xff0c;主要分为应用崩溃、应用卡死两种。 效果图预览 使用说明 点击构建应用崩溃事件&#xff0c;3s之后应用退出&#xff0c;然后打开应用进入应用异常页面&#xff0c;隔1mi…