享元模式详解:解锁高效资源管理的终极武器

news2024/12/22 20:08:56

🎯 设计模式专栏,持续更新中
欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

享元模式

享元模式(Flyweight Pattern) 是一种结构型设计模式,旨在通过共享细粒度对象来减少内存使用和对象创建的开销。享元模式可以在系统中重复使用多个相同或相似的对象,通过避免重复创建相同的对象来提高性能,特别是在大量对象需要频繁创建时,享元模式能够极大减少内存消耗。

核心思想:

享元模式将对象分为内部状态外部状态,其中内部状态可以被共享,而外部状态则由外部提供。通过共享内部状态对象,可以避免创建大量类似的对象。

关键点:

  • 内部状态:可以被共享的状态,不会随着环境改变。
  • 外部状态:根据具体场景变化的状态,通常通过外部传递给享元对象。
  • 享元工厂:用于管理和维护享元对象的共享,确保重复对象不会多次创建。

享元模式的原理类图

在这里插入图片描述

  1. FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态的接口或实现
  2. ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
  3. UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
  4. FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法

生动的案例:图形绘制系统 🎨

假设我们正在开发一个图形绘制系统,需要在屏幕上绘制大量的圆形(Circle)。每个圆形都有颜色、半径、位置等属性。为了提高性能,我们使用享元模式,共享相同颜色的圆形对象,避免重复创建相同颜色的圆形。

  • 共享对象:具有相同颜色的圆形可以共享。
  • 外部状态:圆形的半径和位置(这些信息是具体到每个圆形的,不能共享)

代码实现

Step 1: 创建 Flyweight 接口

定义享元模式的接口,draw() 方法接收外部状态。

// 享元抽象类
public interface Flyweight {
    void draw(int x, int y, int radius); // 外部状态为位置和半径
}

Step 2: 实现具体的 Flyweight

实现 Flyweight 接口,ConcreteFlyweight 包含共享的颜色属性。

// 具体享元类
public class ConcreteFlyweight implements Flyweight {
    private final String color; // 共享的内部状态

    public ConcreteFlyweight(String color) {
        this.color = color;
    }

    @Override
    public void draw(int x, int y, int radius) {
        System.out.println("Drawing a " + color + " circle at (" + x + ", " + y + ") with radius " + radius);
    }
}

Step 3: 实现享元工厂类

工厂类负责管理共享的 Flyweight 对象,确保同样颜色的圆形只创建一次。

public class FlyweightFactory {
    private static final Map<String, Flyweight> flyweights = new HashMap<>();

    public static Flyweight getFlyweight(String color) {
        Flyweight flyweight = flyweights.get(color);
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(color);
            flyweights.put(color, flyweight);
            System.out.println("Creating a " + color + " flyweight.");
        }
        return flyweight;
    }
    public static int getFlyweightCount(){
        return flyweights.size();
    }
}

Step 4: 客户端使用享元模式

客户端通过工厂获取 Flyweight 对象,并传入外部状态(位置和半径)。

public class Client {
    public static void main(String[] args) {
        Flyweight circle1 = FlyweightFactory.getFlyweight("Red");
        circle1.draw(10, 10, 5);

        Flyweight circle2 = FlyweightFactory.getFlyweight("Red");
        circle2.draw(20, 20, 10);

        Flyweight circle3 = FlyweightFactory.getFlyweight("Blue");
        circle3.draw(15, 15, 7);

        Flyweight circle4 = FlyweightFactory.getFlyweight("Red");
        circle4.draw(30, 30, 15);
        System.out.println("-------------------------------------");
        System.out.println("Total number of flyweights: " + FlyweightFactory.getFlyweightCount());
    }
}

输出结果

Creating a Red flyweight.
Drawing a Red circle at (10, 10) with radius 5
Drawing a Red circle at (20, 20) with radius 10
Creating a Blue flyweight.
Drawing a Blue circle at (15, 15) with radius 7
Drawing a Red circle at (30, 30) with radius 15
-------------------------------------
Total number of flyweights: 2

享元模式在源码中的应用

享元模式广泛应用于 Java 标准库和一些优秀的开源框架中,主要用于优化性能、减少内存占用。以下是几个使用了享元模式的经典例子:

1. Java 中的 String 常量池

Java 中的 String 类型是享元模式最典型的应用之一。String 类在 Java 中是不可变的,JVM 会在字符串常量池中缓存相同的字符串对象,以避免重复创建相同内容的字符串,从而节省内存空间。

享元模式的应用

  • 内部状态:字符串内容(相同的字符串内容可以被共享)。
  • 外部状态:无(因为 String 是不可变的,所有内容都可以共享)

示例代码

public class StringFlyweightExample {
    public static void main(String[] args) {
        String s1 = "Hello";
        String s2 = "Hello";

        // s1 和 s2 是同一个对象,因为 JVM 在常量池中共享了 "Hello"
        System.out.println(s1 == s2);  // 输出 true

        // 通过 new 关键字创建新的字符串对象,不会使用常量池
        String s3 = new String("Hello");
        System.out.println(s1 == s3);  // 输出 false
    }
}

JVM 通过字符串常量池优化内存使用,避免重复创建相同内容的字符串。如果想看更深度解读String,请看这篇文章 https://blog.csdn.net/qq_44732500/article/details/141884904

2.Java 中的 Integer 缓存池

Integer 类中也使用了享元模式,对**-128 到 127 之间的整数**进行了缓存。这意味着当我们创建这些范围内的 Integer 对象时,它们将被共享,而不是每次都创建新对象。

享元模式的应用

  • 内部状态:整数值(-128 到 127 之间的整数被缓存共享)。
  • 外部状态:无(这些整数是不可变的)。

源码片段

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

示例代码

public class IntegerFlyweightExample {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf(127);
        Integer i2 = Integer.valueOf(127);

        // i1 和 i2 是同一个对象,因为 127 在缓存范围内
        System.out.println(i1 == i2);  // 输出 true

        Integer i3 = Integer.valueOf(128);
        Integer i4 = Integer.valueOf(128);

        // i3 和 i4 不是同一个对象,因为 128 不在缓存范围内
        System.out.println(i3 == i4);  // 输出 false
    }
}

Integer 缓存池通过共享 -128127 范围内的整数,减少了内存使用。

3.MyBatis 中的 SqlSessionFactory

MyBatis 是一个持久层框架,它通过享元模式优化了 SqlSessionFactory 对象的创建。SqlSessionFactory 是一个重量级对象,通常一个应用只需要一个实例。MyBatis 通过工厂模式和享元模式确保每个数据库只创建一个 SqlSessionFactory 实例,并重复使用。

享元模式的应用:

  • 内部状态SqlSessionFactory 对象(共享的连接配置)。
  • 外部状态:无(SqlSessionFactory 本身是不可变的)。

示例代码

public class MyBatisFlyweightExample {
    public static void main(String[] args) {
        SqlSessionFactory sessionFactory1 = MyBatisUtil.getSqlSessionFactory();
        SqlSessionFactory sessionFactory2 = MyBatisUtil.getSqlSessionFactory();

        // sessionFactory1 和 sessionFactory2 是同一个实例
        System.out.println(sessionFactory1 == sessionFactory2);  // 输出 true
    }
}

MyBatis 通过共享 SqlSessionFactory 实现了享元模式,避免重复创建数据库连接工厂,节省了系统资源。

4.Java 数据库连接池

在 Java 应用中,数据库连接池(如 HikariCP、DBCP 等)也是享元模式的经典应用。数据库连接是非常昂贵的资源,连接池通过共享有限的连接对象,避免重复创建和销毁连接,提升了系统性能。

享元模式的应用

  • 内部状态:连接池中的连接对象(可以共享)。
  • 外部状态:连接的具体使用状态(如连接是否空闲)。

代码概念

// 数据库连接池 Flyweight 模式
HikariDataSource dataSource = new HikariDataSource();
Connection conn1 = dataSource.getConnection();
Connection conn2 = dataSource.getConnection();

// 使用同一个数据库连接池管理连接对象

数据库连接池通过享元模式复用连接对象,大大提高了数据库连接的性能和资源管理。

总结

  1. 减少内存消耗:通过共享相同对象,减少重复对象的创建,特别适合大量相似对象的场景。
  2. 提高系统性能:通过共享对象,减少内存使用,提升程序的运行效率。
  3. 增加系统复杂性:为了区分内部状态和外部状态,系统设计可能会更加复杂
  4. 适用场景有限:享元模式适用于存在大量相同或相似对象的场景,如果对象的状态不易共享,享元模式的效果有限。

使用场景

  1. 需要大量重复对象的场景:如图形绘制系统、文本编辑器中字符对象的管理。
  2. 缓存池:享元模式常用于对象池或缓存池中,避免创建大量相同的对象。
  3. 游戏开发:在游戏中,地图上的草地、树木等对象可以使用享元模式进行共享。

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

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

相关文章

yaml配置文件(SpringBoot学习4)

SpringBoot使用一个全局的配置文件&#xff0c;配置文件名是固定的 application.properties 语法结构&#xff1a;keyvalue application.yaml 语法结构&#xff1a;key:空格value #是注释 #yaml 普通的key - value 例&#xff1a;name&#xff1a;…

力扣题解2390

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述​&#xff08;中等&#xff09;&#xff1a; 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中&#xff0c;你可以&#xff1a; 选中 s 中的一个星号。 移除星号…

23. Revit API: 几何对象(四)- BrepBuilder

一、前言 上一篇写了Solid的创建、展示、变换、布尔操作&#xff0c;这一篇写另一种Solid的创建方法。 需要再次强调的是&#xff0c;BrepBuilder不适合用来创建Solid。 二、边界表示 【Wiki】&#xff1a;边界表示&#xff08;Boundary representation&#xff0c;简称B-Re…

顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测

顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测 目录 顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测&#xff08;程序可以作为JCR…

外贸展会全流程、如何高效转化,获取更多名片

马上又到展会季了&#xff0c;九十月份是各行各业的展会旺季&#xff0c;很多伙伴们比较关注的是&#xff0c;展会上如何拿到更多名片&#xff0c;如何高效转化客户 一、展会的好处 1、扩展客户资源&#xff1a;接触潜在客户、扩大市场覆盖 2、提升品牌知名度&#xff1a;展示…

学生学籍管理系统可行性分析报告

引言 一、编写目的 随着科学技术的不断提高,计算机科学日渐成熟,其强大的功能已为人们深刻认识,它已进入人类社会的各个领域并发挥着越来越重要的作用。而学籍管理系统软件&#xff0c;可广泛应用于全日制大、中小学及其他各类学校&#xff0c;系统涵盖了小学、初中、高中学籍…

122.rk3399 uboot(2017.09) 源码分析2-initf_dm(2024-09-09)

这里接着上一篇来吧&#xff1a; https://blog.csdn.net/zhaozhi0810/article/details/141927053 本文主要是dm_init_and_scan函数的分析&#xff0c;这个内容比较复杂&#xff0c;我也是第一次阅读&#xff0c;错误之处在所难免&#xff0c;请多指教。 uboot的dm框架需要了解…

去除单细胞数据中环境游离的RNA污染-decontX工具学习

DecontX 是一种用于单细胞 RNA 测序数据的去除环境污染物&#xff08;decontamination&#xff09;的工具&#xff0c;主要用于减少由细胞外RNA造成的污染效应。 开发者在20年的文章中已经把这个工具适用的情况说的非常清楚了&#xff1a;简单来说就是基于微流控的单细胞技术会…

解决PowerAutomate日期处理报错

问题来源 今天尝试做一个简单到到不能再简单的PowerAutomate流&#xff0c;就是读取一个Sharepoint上的Excle表格里的每一行&#xff0c;然后更新到一个list。然鹅确收到了一个意想不到的报错&#xff0c; 报错信息如下&#xff1a; The runtime value "" to be c…

Android 签名、空包签名 、jarsigner、apksigner

jarsigner是JDK提供的针对jar包签名的通用工具, 位于JDK/bin/jarsigner.exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具, 位于Android SDK/build-tools/SDK版本/apksigner.bat jarsigner&#xff1a; jarsigner签名空包执行的命令&#xff1a; jar…

关于Spring Cloud Gateway中 Filters的理解

Spring Cloud Gateway中 Filters的理解 Filters Filters拦截器的作用是,对请求进行处理 可以进行流量染色 ⭐增加请求头 例子 spring:cloud:gateway:routes:- id: add_request_header_routeuri: http://localhost:8123predicates:- Path=/api/**filters:- AddRequestHea…

嵌入式软件工程师:科技浪潮中的关键角色

嵌入式软件工程师&#xff1a;科技浪潮中的关键角色 一、嵌入式软件工程师的职业魅力 &#xff08;一&#xff09;市场需求旺盛 嵌入式软件工程师在当今科技领域中扮演着至关重要的角色。随着智能化时代的到来&#xff0c;嵌入式系统在各个行业的应用越来越广泛&#xff0c;市…

Java API 搜索引擎测试报告

一、测试项目介绍 基于SpringBoot开发的 Java API 文档搜索引擎&#xff0c;输入具体的类名或包名就能找到对应相关的搜索结果&#xff0c;点击标题即可跳转到对应官方网页。 二、测试 测试环境&#xff1a;Windows11&#xff0c;Google chrome浏览器 128.0.6613.138 (正式版…

jmeter 录制APP脚本

一、手机 1、修改网络 代理选择手动→填写服务器主机名&#xff08;电脑IP&#xff0c;如&#xff1a;192.1xx.x.xx&#xff09;→服务器端口&#xff08;任意未被占用端口&#xff0c;如&#xff1a;8888&#xff09; 2、安装证书 手机浏览器访问服务器主机名:服务器端口&a…

通过mqtt通信远程控制大疆无人机

一、控制大疆无人机通信链路如上图所示 二、大疆无人机pilot指令飞行通信交互逻辑如上图所示 三、实现远程控制步骤 3.1前端单独实现&#xff1a;只通过后端获取控制权&#xff0c;然后前端单独发送mqtt指令&#xff0c;延时较低。 3.2前后端一起实现&#xff1a;前端发送ht…

Leetcode - 周赛414

目录 一&#xff0c;3280. 将日期转换为二进制表示 二&#xff0c;3281. 范围内整数的最大得分 三&#xff0c;3282. 到达数组末尾的最大得分 四&#xff0c;3283. 吃掉所有兵需要的最多移动次数 一&#xff0c;3280. 将日期转换为二进制表示 本题就是简单的字符串和整数之…

产品密封防水设计技术、工艺标准及实施方案解培训

一、课程背景&#xff1a; 当前智能产品密封防水能力成为电子产品营销的重要卖点&#xff0c;虽然我们不需长期带着某种电子设备在水下工作和生活&#xff0c;意外却会令价值不菲的电子产品瞬间报废。时下智能手表和耳机成了慢跑伴侣&#xff0c;汗水或雨水长期的侵润腐蚀&…

查谷歌流量什么最准确,服务商提供的工具为什么不能用?

查网站的SEO流量&#xff0c;Google Search Console是最准确的工具&#xff0c;因为这就是谷歌官方提供的工具&#xff0c;谷歌这方面没必要造假&#xff0c;GSC能直接展示你的网站在谷歌搜索中的表现&#xff0c;包括点击次数、展示次数、点击率和平均排名。因为这些数据直接来…

项目需求 | MySQL增量备份与恢复的完整操作指南

目录 一、MySql数据库增量备份的工作原理 1、全量备份与增量备份 2、增量备份原理 二、进行增量备份 步骤1&#xff1a;启用二进制日志 使用 SHOW VARIABLES 命令查看二进制日志状态 步骤2&#xff1a;执行增量备份脚本 三、使用增量备份恢复损坏的数据库 步骤1&#…

[数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9769 标注数量(xml文件个数)&#xff1a;9769 标注数量(txt文件个数)&#xff1a;9769 标注…