设计模式(三)-结构型模式(6)-享元模式

一、为何需要享元模式(Flyweight)?

假如在网页中渲染这样的一个画面:大小不一的星星铺满了整个画布,并且都在不断的进行移动闪烁着。一批星星消失了,另一批又从另一边缘处出现。

请添加图片描述

要实现这样的渲染效果,在程序中就得需要创建这些星星,然后将它们一个个画上去。

有个问题就是,如果我们还得按照平常创建对象的方式,对每一颗出现的星星都创建一遍。一旦星星从画布上消失,就销毁并释放内存。这么做的话,系统就得要开销大量的内存空间,而且频繁进行创建销毁的操作会影响程序运行的性能。到最后我们只能看到这样的效果:一打开网页后,画布上的那些星星隔了一段时间才全部出现,滚动网页也不流畅。

为了解决“隔了一段时间才全部出现”和“滚动网页也不流畅”的问题(因为存储大量对象而导致开销大量内存、频繁创建销毁对象而导致程序的性能下降),我们就应该尽可能少的生成那些存在相同状态的星星。

其实在繁星点点的夜空中,总会有很多相似或者相同的星星存在着。因此我们可以只创建一颗星星作为一个共享对象,来代表这些与之相似的同类星星。在渲染的时候,只对这个共享对象重复的画上去就行了。

享元模式的定义:运用共享技术有效的支持大量细粒度的对象。

从以上的定义,我们先分析“享元”和”细粒度“这两个关键词。

  • 享元即共享元对象。元这个词,就好比数据库表中的一组若干个元数据,元数据是数据的最小单位。所以元对象的意思也就是,一组同类对象中的每一个元对象。既然这些同类对象都是相同状态的对象,我们只需要创建一个共享元对象来代表它们。
  • 细粒度如颗粒大小一样,即这个对象并不庞大而复杂。所以享元对象就应当是结构简单的对象。

但是我们怎么区分对象之间是否存在相同状态,然后将他们归类为某一组同类对象呢。于是可以对这些对象内的某个属性作为唯一标识,来判断是否为同类对象。比如星星,唯一标识可以是星星的半径值,也可以是速度值等。在这里我们选择的是以半径作为唯一标识。如图所示:
(在享元对象容器,每一个享元对象之间的半径值不相同,都代表着各自一组与自己半径相同的星星。)

请添加图片描述

享元对象是有了,但是我们编程中,起码也要保证唯一标识的半径值,是不能因为程序的变化而变化吧,即对象的外部不能对半径进行修改。这时候就有了外部状态和内部状态的区别。

外部状态: 存在于享元对象的外部。因环境变化而变化。(环境变化即客户端发生的状态变化)
内部状态: 存在于享元对象的内部。不能因环境变化而变化。

  • 外部状态是客户端进行的活动变化,比如为星星的位置进行随机布局,控制星星消失和出现的个数等等。

  • 内部状态是对象固有的状态,即一颗半径大小为4的星星,半径大小是固有的,客户端把它画上去时,不能强行使它变大变小。要保持内部状态,对象内的所有属性和状态都应当被保护起来,如对象内的所有字段都被设置为私有的访问机制。

特点:

  • 减少创建对象的数量,使用享元对象来代表一组同类对象。

结构:
抽象享元(Flyweight):具体享元类的基类。规定了具体享元要实现的方法,,也可以接受并作用于外部状态。(接受并作用于:传外部状态的参数到该方法内,以处理外部状态,但不改变享元内部状态。)
具体享元类(ConcreteFlyweight):实现抽象享元的方法。如果存在内部状态(即在享元容器里没有存在该享元时,就实例化一个),则增加存储空间。
享元工厂类(FlyweightFactory):创建和管理享元对象。
客户端类(Client):所有享元对象的引用,并存储对应的外部状态。(引用:把星星画上去;存储外部状态:存储星星的位置和出现的个数等)

适合应用场景特点:

  • 需要大量细粒度的对象,来完成某个功能。
  • 大量对象有存在着相同的状态。
  • 实际例子:线程池中的共享线程,解决频繁创建销毁线程的问题,字符串常量池中的共享字符串,解决重复创建存在值相同的字符串的问题…

请添加图片描述

二、例子

需求:

在视频剪辑中,用户想要在画布上实现满天星的效果,就做了如下参数的设置:
1)星星的总数为1000000,并且这些星星的大小和个数都是随机的;
2)星星的半径大小在 1~10 范围;
3)勾选默认星星的速度、闪烁频率、亮度都跟半径的大小有关,所以不用用户来自定义这些参数的值。

设计分析:

  • 以半径作为唯一标识,一个享元对象对应一组半径相同的对象。
  • 享元对象最多有 10 个。需要渲染的对象有1000000 个。

1、定义抽象享元和具体享元类

//Flyweight:抽象享元(星星抽象)public interface IStar{void draw();}//ConcreteFlyweight:具体享元类(星星)public class Star:IStar{private int Radius;private double Brightness;private double Twinkle;private double Speed;public Star(int radius){Radius = radius;//Brightness = ...;//Twinkle = ...;//Speed = ...; }//1、args:外部状态可作为参数传入,但不能改变 Star 的内部状态public void draw(/*args:若有外部实例传入*/){//处理一些与外部有关的逻辑...//外部参数不能改变 Star 类的所有属性值和其他内部状态}//2、如果该方法在客户端进行调用,而不是在享元工厂创建 Star 类的享元对象时调用://那么该方法的内部逻辑是错误的,是因为不能通过外部 val 改变 Brightness 值。public void setBrightness(double val){Brightness = val;}}

2、定义享元工厂类

//FlyweightFactory:享元工厂类(创建和管理享元对象)public class FlyweightFactory{//享元对象的容器private Dictionary<int, IStar> StarsDict = new Dictionary<int, IStar>();//创建和获取享元对象,radius 为标识享元对象的参数。public IStar getStar(int radius){IStar star = null;//在容器中是否存在跟 radius 值相同的对象StarsDict.TryGetValue(radius,out star);if(star == null)//若不存在,则创建一个享元并存入到容器里{star = new Star(radius);StarsDict.Add(radius,star);}return star;}}

3、主程序


//主程序class Program{static void Main(string[] args){Random random = new Random(10);//享元模式-----------------FlyweightFactory factory = new FlyweightFactory();for (int i = 0; i < 1000000; i++){//随机生成半径大小为1~10范围的星星。var star = factory.getStar(random.Next(1, 10));star.draw();//对外部状态的影响}//-------------------------//非享元模式----------------List<IStar> starList = new List<IStar>();for (int i = 0; i < 1000000; i++){//创建了 1000000 个对象,执行整个循环的时间久。var star = new Star(random.Next(1, 10));starList.Add(star);star.draw();}//--------------------------//验证非享元模式和享元模式,在当前程序里使用内存大小的情况:var process = Process.GetCurrentProcess();var memorySize = process.PrivateMemorySize64 / (1024 * 1024);//单位为 M.Console.WriteLine(memorySize);//某次运行验证的结果://享元模式,内存占用:17M;非享元模式,内存占用:60M;Console.ReadLine();}}

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

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

相关文章

『居善地』接口测试 — 20.Mock功能介绍

1、Mock功能介绍 各个业务系统都会关联多个三方系统接口调用&#xff0c;在测试过程中第三方业务存在不能及时提供接口调用&#xff0c;这时就需要用到我们的mock服务了。 Mock的本质在于模拟三方业务接口的返回&#xff0c;来满足自身的测试功能&#xff0c;快速完成测试任务…

docker安装ES:7.8和Kibana:7.8

本文适用于centos7,快速入手练习es语法 前置&#xff1a;安装docker教程docker、docker-component安装-CSDN博客 1.安装es 9200为启动端口&#xff0c;9300为集群端口 docker pull elasticsearch:7.8.0mkdir -p /mydata/elasticsearch/pluginsmkdir -p /mydata/elasticsear…

python核心阶段(七)—— 包&模块以及虚拟环境

1.包&模块 概念解释 模块&#xff1a;为了使代码容易维护&#xff0c;可以将一组功能相关的代码写入一个单独的.py文件中&#xff0c;这 个.py文件就被称作一个模块 包&#xff1a; 包是指一个有层次的文件目录结构&#xff0c;它包含多个相关模块或子包&#xff1b; 它…

基于MybatisPlus批量高效插入百万条数据

引言 在JAVA程序开发中&#xff0c;对数据库进行大量数据插入是一个常见的操作&#xff0c;作为一个软件开发工程师&#xff0c;大批量的数据处理是日常工作&#xff0c;如何优化插入性能&#xff0c;提升数据处理效率是对大多数工程师的一个重要考验。本文将围绕逐条插入和批…

随时随地安心工作:迅软DSE保护您手机办公中的关键数据

互联网的快速发展让移动办公成为了日常工作中的一部分&#xff0c;同时企业数据加密的需求也已经不仅仅局限于内部终端&#xff0c;对于灵活的手机移动端也同样需要进行合法合规的数据安全管控。 迅软DSE数据防泄密系统提供移动端管理模块&#xff0c;支持Android、IOS移动客户…

BearPi Std 板从入门到放弃 - 引气入体篇(11)(SPI驱动 TFT LCD(ST7789))

简介 SPI 驱动 ST7789V2 进行字符显示, 并且使用中文库显示中文信息。主芯片: STM32L431RCT6LED : PC13 \ 推挽输出即可 \ 高电平点亮串口: Usart1 / LPUARTSPI(与LCD数据传输) : SPI2LCD_RESET&#xff08;复位引脚&#xff09;: PC7 \ 推挽输出即可 LCD_POWER&#xff08;…

通过几个基本概念说一下为什么openGauss是当下之选?

Database、Schema、User都是数据库的基本概念&#xff0c;SQL标准中也有明确规范。但不同数据库的具体实现也不尽相同&#xff0c;有些甚至大相径庭。这就导致用户在做国产化选型和数据库迁移时可能会遇到种种困难。本文从这几个基本概念展开&#xff0c;说说为什么openGauss系…

CHARLS CLHLS CFPS公共数据库, 最新文章|周报(12.6)

欢迎参加郑老师2023年孟德尔随机化课程即将开始 发表文章后退款&#xff01;郑老师科研统计课程详情 CHARLS公共数据库 CHARLS数据库简介中国健康与养老追踪调查(China Health and Retirement LongitudinalStudy&#xff0c;CHARLS)是一项持续的纵向调查&#xff0c;旨在调查中…

Stable Diffusion Windows 部署简单认知

写在前面 偶然看到&#xff0c;简单了解博文为 SD 部署&#xff0c;以及简单使用&#xff0c;部署过程遇到问题解决理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。…

pnpm :无法加载文件 D:\nodejs\node_global\pnpm.ps1,因为在此系统上禁止运行脚本

目录 一、问题描述 二、原因分析 三、解决问题 一、问题描述 pnpm : 无法加载文件 D:\learningsoftware\nodejs\node_global\pnpm.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID1351 70 中的 a…

大象机器人发布万元级水星Mercury人形机器人产品系列,联结未来,一触即达!

十四五机器人产业发展规划指出机器人的研发、制造、应用是衡量一个国家科技创新和高端制造业水平的重要标志。当前&#xff0c;机器人产业蓬勃发展&#xff0c;正极大改变着人类生产和生活方式&#xff0c;为经济社会发展注入强劲动能。 人形机器人作为机器人产业中重要的一环&…

Java异常类分类,所有子类的父类是什么

1.异常的层次机构&#xff1a; 所有异常的父类是Throwable&#xff0c;它有两个子类&#xff0c;分别是Error和Exception。 2.Error&#xff1a; 表示系统错误&#xff0c;通常不能处理和恢复。比如StackOverFlowError或者OutOfMemoryError&#xff0c;出了问题只能结束程序…

Grafana安装zabbix插件

文章目录 一、在线安装zabbix插件二、离线安装1.下载安装2.grafana配置zabbix数据源 一、在线安装zabbix插件 如果grafana服务器可以联网即可在线安装。 安装方法官网上有&#xff1a;官网链接联网安装比较慢。 二、离线安装 1.下载安装 官网下载链接 [rootnode1 src]# mo…

猫罐头哪个品牌好?五款性价比高的猫罐头推荐

很多新手养猫的姐妹们都会为选罐头感到焦虑&#xff01;但是每种罐头都有优缺点&#xff0c;每只猫咪的胃口也都不同&#xff0c;只有适合自家猫的才是最好的。所以姐妹们在选罐头之前可以先做好功课&#xff0c;了解一下怎么选好的罐头。 作为家里有3只猫的铲屎官来说&#xf…

Text Intelligence - TextIn.com AI时代下的智能文档识别、处理、转换

本指南将介绍Text Intelligence&#xff0c;AI时代下的智能文档技术平台 Textin.com 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认…

Ubuntu 常用命令之 clear 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 clear命令在Ubuntu系统下用于清除终端屏幕的内容。这个命令没有任何参数&#xff0c;它的主要作用就是清理终端屏幕上的所有信息&#xff0c;使得屏幕看起来像是新打开的一样。 使用clear命令非常简单&#xff0c;只需要在终端中…

JavaScript读写15693 ICod2 卡源码

本示例使用设备 &#xff1a; https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-21818769070.11.23eb789efg450Y&id615391857885 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-t…

camera 客观校准 (1)——抓图

#灵感# 客观校准很多平台都有&#xff0c;无非是抓raw&#xff0c;导入到calibration tool 中&#xff0c;设置一些参数&#xff0c;导出相应的校准数据。抓raw的方法&#xff0c;也是大差不差。但有些地方细节容易忽略&#xff0c;所以罗列一下。 目录 blc: lsc: color: …

ELFK日志收集

文章目录 第一章:ELK日志收集系统介绍日志收集重要性ELK介绍EFK介绍ELFK介绍ES部署Kibana部署第二章:Logstach日志收集Logstash介绍Logstash安装Logstash Input输入插件Logstash Filter过滤插件Logstash Output输出插件Input fileFilter mutatesplit示例add_field示例remove_…

ATFX期市:安哥拉宣布退出OPEC,减产计划还能否彻底执行?

ATFX期市&#xff1a;昨日&#xff0c;安哥拉矿产资源、石油和天然气部长迪亚曼蒂诺阿泽维多在首都罗安达宣布&#xff0c;安哥拉决定退出OPEC&#xff0c;原因是此前OPEC第36次会议将安哥拉的产量配额设定为111万桶&#xff0c;不及安哥拉方面提出的118万桶。安哥拉的退出预计…