【设计模式】享元模式(Flyweight Pattern)

news2024/10/6 22:22:53

享元模式属于结构型模式,主要解决系统需要使用大量相似对象(细粒度对象)而消耗大量内存资源的问题。享元模式运用共享技术有效地支持大量细粒度的对象,其通过提取对象共同的信息抽象出享元对象,实现共享功能,以此进行一个对象的多次复用,本质是缓存共享对象,降低内存消耗。


文章目录

  • 享元模式的介绍
    • 优点
    • 缺点
    • 应用场景
  • 享元模式的使用
    • 类图
      • 四种角色
    • 实现方法
      • 第一步,编写非享元
        • 坐标类
      • 第二步,编写抽象享元
        • 图形类
      • 第三步,编写具体享元
        • 圆形类
      • 第四步,编写享元工厂
        • 图形工厂类
      • 第五步,编写客户程序测试
        • 客户程序类
        • 测试结果
    • 本文参考



享元模式的介绍

享元模式(Flyweight Pattern)又被称作轻量级模式蝇量模式,它经典的体现就是对象池。享元模式的根本目的就是"共享单元",让对象能够共享复用,就像共享单车一样,路人甲(客户程序A)骑完路人丙(客户程序B)用,以此减少内存占用

享元模式将对象的信息分为内部状态外部状态:内部状态是存在于享元对象内部的,不会随环境变化而改变的共享信息;外部状态是无法共享的信息,随环境改变而改变,是由调用者传入享元对象中的信息。使用享元模式时需要创建一个工厂(池)对象并持有一个HashMap集合来管理这些享元对象。不同的调用者通过唯一标识(key)获取指定的享元对象来达到共享的功能。

优点

  • 大大减少对象的创建,降低系统的内存,使效率提高

缺点

  • 提高了系统的复杂度,需要分离出内部状态和外部状态
  • 注意划分外部状态和内部状态,否则可能会引起线程安全问题

应用场景

  • 当系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源时
  • 当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多出需要使用的地方,避免大量同一对象的多次创建,降低大量内存空间的消耗
  • 字符串常量池、线程池、缓冲池等池技术
  • 常用于系统底层开发,解决系统的性能问题:如数据库连接池,池中是创建好的连接对象,若池中有符合需求的对象时直接用,避免重新创建;若池中没有符合需求的,则创建一个
  • Integer类中,如果目标值在-128~127之间,则直接从缓存中取值,因为在-128~127之间的数据在int范围内是使用最频繁的,为了节省频繁创建对象带来的内存损耗,这就使用了享元对象



享元模式的使用

举例:现在我要在屏幕上画圆,圆有红、黄、蓝、绿、黑五种颜色,大概要画三十个,那就要建三十个对象,但此时可以使用享元模式设计程序结构,我们将圆的颜色抽取为内部属性,而坐标则是外部属性,由客户程序决定,随后通过图形工厂管理享元对象,相同颜色的圆可以进行复用。


类图

image-20221206201439351

四种角色

  • 抽象享元(Flyweight)角色:是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。

  • 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。

  • 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。

  • 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

此类图中存在一个线程安全问题,也就是半径属性,此属性需要通过客户程序给出,但又属于享元对象的内部属性,当在多线程下,一个线程修改了半径属性,其他线程中的享元对象的该属性也会改变。这也就是上文所说的注意划分外部状态和内部状态,否则可能会引起线程安全问题。解决方法:可以将半径属性去除,在绘画方法中添加面积参数,这样就得到了外部属性面积,而半径可以通过面积与π得出。


实现方法

第一步,编写非享元

坐标类

package 设计模式.结构型模式.享元模式;

/**
 * 非享元角色,它以参数的形式注入具体享元的相关方法中
 */
public class 坐标类 {
    private Double 坐标x, 坐标y;

    public 坐标类(Double 坐标x, Double 坐标y) {
        this.坐标x = 坐标x;
        this.坐标y = 坐标y;
    }

    public Double 获取坐标x() {
        return 坐标x;
    }

    public Double 获取坐标y() {
        return 坐标y;
    }
}

第二步,编写抽象享元

图形类

package 设计模式.结构型模式.享元模式;

/**
 * 抽象享元角色,是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入
 */
public interface 图形类 {
    void 绘画(坐标类 坐标);
}

第三步,编写具体享元

圆形类

package 设计模式.结构型模式.享元模式;

/**
 * 具体享元角色,实现抽象享元角色中所规定的接口
 */
public class 圆形类 implements 图形类{

    private String 颜色;
    private Double 半径; // 此内部属性在多线程中会引起线程安全问题

    public 圆形类(String 颜色) {
        this.颜色 = 颜色;
    }

    /*
        此属性需要通过客户程序给出,但又属于享元对象的内部属性,当在多线程下,一个线程修改了`半径`属性,其他线程中的享元对象的该属性也会改变
        解决办法:可以将"半径"属性去除,在绘画方法中添加"面积"参数,这样就得到了外部属性"面积",而半径可以通过面积与π得出
     */
    public void 设置半径(Double 半径) {
        this.半径 = 半径;;
    }

    @Override
    public void 绘画(坐标类 坐标) {
        // .2f 保留小数点后两位
        System.out.printf("【%s】圆形:坐标(%.2f,%.2f) 半径%.2f \n",颜色,坐标.获取坐标x(),坐标.获取坐标y(),半径);
    }
}

第四步,编写享元工厂

图形工厂类

package 设计模式.结构型模式.享元模式;


import java.util.HashMap;

public class 图形工厂类 {

    private static HashMap<String, 图形类> 图形集合 = new HashMap<>() ;

    private 图形工厂类(){}

    public static 图形类 获取图形(String 颜色){
        图形类 图形 = 图形集合.get(颜色);
        // 如果图形集合中没有该共享对象的原型,则创建一个
        if (图形 == null){
            图形 = new 圆形类(颜色);
            图形集合.put(颜色, 图形);
        }
        return 图形;
    }
}

第五步,编写客户程序测试

客户程序类

package 设计模式.结构型模式.享元模式;

public class 客户程序类 {

    private static final String[] 颜色列表 = new String[]{"红","黄","蓝","绿","黑"};

    public static void main(String[] args) {
		// 创建30
        for (int i = 0; i < 30; i++) {
            坐标类 随机坐标 = new 坐标类(Math.random()*100, Math.random() * 100);
            圆形类 圆形 = (圆形类) 图形工厂类.获取图形(获取随机颜色());
            圆形.设置半径(Math.random()*100);
            圆形.绘画(随机坐标);
        }
    }

    private static String 获取随机颜色(){
        return 颜色列表[(int) (Math.random()*颜色列表.length)];
    }

}

测试结果

【绿】圆形:坐标(83.22,92.62) 半径38.38 
【红】圆形:坐标(72.57,95.39) 半径96.26 
【黑】圆形:坐标(18.29,22.11) 半径33.26 
【黑】圆形:坐标(48.28,32.95) 半径9.79 
【黑】圆形:坐标(25.65,91.99) 半径21.51 
【黄】圆形:坐标(92.08,18.76) 半径48.55 
【黑】圆形:坐标(35.10,20.12) 半径0.53 
【黄】圆形:坐标(57.73,99.47) 半径90.03 
【黑】圆形:坐标(3.87,75.13) 半径41.67 
【蓝】圆形:坐标(46.09,78.58) 半径71.46 
【黑】圆形:坐标(73.91,44.40) 半径51.43 
【黄】圆形:坐标(30.00,48.53) 半径8.46 
【黑】圆形:坐标(29.40,99.16) 半径93.00 
【红】圆形:坐标(62.56,35.63) 半径93.06 
【蓝】圆形:坐标(26.88,44.36) 半径62.43 
【黑】圆形:坐标(53.38,58.30) 半径77.71 
【黄】圆形:坐标(95.18,66.50) 半径38.43 
【黄】圆形:坐标(22.78,23.79) 半径76.93 
【黑】圆形:坐标(27.47,17.04) 半径50.00 
【黄】圆形:坐标(68.51,14.11) 半径43.27 
【黑】圆形:坐标(12.65,94.49) 半径31.03 
【红】圆形:坐标(4.87,36.21) 半径99.06 
【绿】圆形:坐标(78.40,12.93) 半径56.09 
【黄】圆形:坐标(58.96,37.03) 半径30.91 
【黑】圆形:坐标(43.07,69.88) 半径76.29 
【黄】圆形:坐标(33.85,8.76) 半径78.20 
【蓝】圆形:坐标(98.77,51.53) 半径99.75 
【绿】圆形:坐标(73.40,34.64) 半径72.56 
【绿】圆形:坐标(72.90,37.99) 半径85.42 
【蓝】圆形:坐标(88.49,50.77) 半径9.74 

Process finished with exit code 0

在这里插入图片描述



本文参考

享元模式 (菜鸟教程)

26 设计模式——享元模式(详解版)(知乎)

享元模式详解 (csdn)

享元模式 (博客园)

五分钟学设计模式.14.享元模式 (哔哩哔哩)

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

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

相关文章

mysql详解之B树的查询时间复杂度

前言 本文是我在看了这篇文章之后&#xff08;这篇文章对b树的时间复杂度总结的很全面&#xff09;&#xff0c;关于B树&#xff08;或B树&#xff09;时间复杂度做的进一步思考&#xff08;如果对解题过程不感兴趣&#xff0c;可以直接看最后的总结&#xff09;。 正题 在这…

Java继承(extends)简明教程

继承是面向对象的三大特征之一。继承和现实生活中的“继承”的相似之处是保留一些父辈的特性&#xff0c;从而减少代码冗余&#xff0c;提高程序运行效率。 Java 中的继承就是在已经存在类的基础上进行扩展&#xff0c;从而产生新的类。已经存在的类称为父类、基类或超类&…

物联网和大数据可应用在哪些领域?

物联网和大数据可应用在哪些领域&#xff1f;物联网和大数据是近年来最受媒体和企业关注的两大宏观技术趋势。两者也并驾齐驱&#xff0c;物联网旨在特定组织或环境中创建一个互联网络&#xff0c;使用该网络来收集数据并集中执行特定功能。物联网部署会生成大量以前未开发的数…

【MR】C++ bullet客户端基于MR的动力学仿真

程序基于&#xff08;A minimal example showing how to use PyBullet from C over shared memory.一个展示如何在共享内存上从c使用PyBullet的最小示例。https://github.com/erwincoumans/pybullet_cpp_sharedmemory_example&#xff09;实现与pybullet服务器通信交互。加上MR…

MySql按条件插入数据,MySQL插入语句写where条件,MySQL在插入时做幂等

文章目录写在前面根据条件插入数据1、先准备测试数据2、正常的插入语句3、有条件的插入语句&#xff08;重点&#xff09;4、查看最终结果总结分析写在前面 使用MySQL的删、改、查功能时&#xff0c;我们都可以根据where条件来对指定数据进行操作。 插入语句如何通过where条件…

Git —— 关于创建多对非对称公钥时对不同服务器的匹配问题

Git —— 关于创建多对非对称密钥时对不同服务器的匹配问题 《工欲善其事&#xff0c;必先利其器》—— 既然点进来了&#xff0c;麻烦你看下去&#xff0c;希望你有不一样的收获~ 《 前言 》 大概是半年前&#xff0c;我写过一篇关于 git 连接托管平台提示 permission denied…

经典算法冒泡排序之标志位优化版

前言 今天总结一下优化版的经典算法——冒泡排序&#xff0c;不同于以往的暴力二重for循环&#xff0c;这里的冒泡排序增加了一个标志位。我们要理解该冒泡排序的概念&#xff0c;算法流程与算法思想&#xff0c;探讨时间复杂度。 冒泡排序算法解析 一、理解冒泡排序思想 1…

Selenium4+Python3系列(十一) - Page Factory设计模式

写在前面&#xff1a; Page Object模式&#xff0c;目的是将元素定位和元素操作分层&#xff0c;只接触测试内容&#xff0c;不写基础内容&#xff0c;便于后续对自动化测试用例体系的维护&#xff0c;这是中心思想&#xff0c;也是核心。 那么我们继续将简洁延续&#xff0c…

Allegro如何铺网格铜详细操作指导

Allegro如何铺网格铜的三种方法操作指导 在做PCB设计的时候,时常需要铺网格铜,尤其是对于绕性有要求的时候,Allegro可以快捷铺出网格铜,类似下图 具体操作如下 直接铺网格铜 选择Shape add Rect命令 Option Type选择Static Crosshatch 画出需要的图形 鼠标右击done,…

详解go语言包管理方式(go mod), 分析多目录多文件下的管理,不同工程下包的相互调用

先明白一个点&#xff0c; 想要成功运行一个go程序&#xff0c; 有两种模式 一&#xff1a; 以前的默认模式&#xff0c;必须将项目放在gopath/src下 二&#xff1a;使用go mod 包管理方式&#xff0c; 项目可以放到任意位置&#xff0c;这样目录下需要有go.mod文件 下边我会分…

毕业设计 stm32单片机的目标检测与跟踪系统 -物联网 openmv 嵌入式

文章目录0 前言课题简介设计框架3 硬件设计4 软件设计对被测物体的识别判断被测物体所在区域5 最后0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年…

力扣(LeetCode)1812. 判断国际象棋棋盘中一个格子的颜色(C++)

数学 如果我们把国际象棋的横轴看做从 111 到 888 &#xff0c;那么每个棋子的坐标形如 a1(1,1)a1(1,1)a1(1,1) c4(3,4)c4(3,4)c4(3,4) &#xff0c;想想坐标之和有没有什么规律。 规律 : 黑子坐标之和为偶数&#xff0c;白子坐标之和为奇数。 横轴坐标是我们自定义的&#x…

基于蚁群算法的三维路径规划算法以及蚁群算法的优化计算——TSP优化(Matlab代码实现)

目录 1 概述 1.1简介 1.2 改进的蚁群算法 2 部分运行结果 2.1 三维路径规划算法 2.2 TSP优化算法 3 Matlab代码实现 4 参考文献 1 概述 1.1简介 当前社会, 很多用户需要在复杂的没有公路的山地地形, 快速、准确的规划出三维路径, 在避过障碍的同时达到某项指标最优。…

【C++笔试强训】第四天

文章目录选择题编程题选择题 #include<iostream> #include<cstdio> using namespace std; int main(){int m0123, n123;printf("%o %o\n", m, n);return 0; }程序运行后的输出结果是&#xff08;&#xff09; A 0123 0173 B 0123 173 C 123 173 D 173 1…

RabbitMQ基本使用

先会用&#xff0c;知道mq是干嘛的&#xff0c;怎么用RabbitMQ 再去考虑一系列深入东西 一&#xff1a;什么是MQ MQ消息队列详解、四大MQ的优缺点分析_从百草园杀到三味书屋&的博客-CSDN博客 理解什么是MQ MQ在企业/程序中的作用是什么&#xff1f; MQ&#xff0c;中文是…

Fluent的porous jump边界条件

1 porous jump简介 多孔介质指内部具有连通的空洞&#xff0c;可使流体穿过的固体。多孔介质具有以下特点&#xff1a; 流道极复杂&#xff0c;活性炭过滤器等随机排布的多孔介质甚至不存在可通过曲面和实体表征的确定流道多孔介质只是流场的一部分&#xff0c;通常不关注多孔…

DataSphere Studio数据应用开发管理集成框架【DSS基础】

https://github.com/WeBankFinTech/DataSphereStudio/https://gitee.com/WeBank/DataSphereStudio 基于插拔式的集成框架设计&#xff0c;及计算中间件 Linkis &#xff0c;可轻松接入上层各种数据应用系统&#xff0c;让数据开发变得简洁又易用。在统一的 UI 下&#xff0c;Da…

[附源码]Python计算机毕业设计Django中小学课后延时服务管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

前端_Vue_2.创建一个Vue应用、模板语法

文章目录一、创建一个Vue应用1.1. 应用实例1.2. 根组件1.3. 挂载应用1.3.1. DOM中的根组件模板1.4. 应用配置1.5. 多个应用实例二、模板语法2.1. 文本插值2.2. 原始HTML2.3. Attribute绑定2.3.1. 简写2.3.2. 布尔型 Attribute2.3.3. 动态绑定多个值2.4. 使用JavaScript表达式2.…

记录一次Sql性能优化

场景&#xff1a; 主业务表 contract&#xff08;合同表&#xff09;&#xff0c;对于不同主体&#xff08;人员&#xff09;&#xff0c;能查看的合同是不一样的。系统企业业务用到了&#xff0c;系统资源表 PERMISSION_RESOURCE 、员工对于资源关系表&#xff1a;ENTRY_JOIN…