设计模式(三)-结构型模式(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.hqwc.cn/news/295593.html

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

相关文章

【大数据HA】HAProxy实现thrift协议HMS服务的高可用-附Chatgpt协助截图

背景 之前安装了HMS(Hive metastore service)&#xff0c;独立于hive运行&#xff0c;安装部署过程见我下面列出的另一篇文章&#xff0c;需要为它建立HA高可用功能。防止在访问时出现单点故障问题。 【大数据】Docker部署HMS(Hive Metastore Service)并使用Trino访问Minio-C…

VS Code实现“Ctr+save”保存代码自动格式化

一、下载Prettier - Code formatter插件 点击安装即可 二、配置 【1】打开文件——首选项——设置 或者左下角齿轮打开设置 【2】搜索设置框输入editor default formatter&#xff08;意思是默认格式化设置&#xff09;&#xff0c;接着下拉选中刚下好的插件名称Prettier - C…

C++初级学习六——数组

一维数组 定义&#xff1a; 1、数组是把具有相同类型的若干变量按有序的形式组织起来的集合。 2、数组是一组用来存放多个相同类型的数据集合&#xff0c;该集合中的每一个成员称为数组元素。 3、通过数组名和一个下标唯一确定的称…

ChatGPT的GPTs是什么?

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 ​ 在 OpenAI 的DevDay&#xff08;11 月 6日&#xff09;&#xff0c;该公司宣布推出 ChatGPT GPT&#xff1a;任何人都可以制作并与他人共享的 ChatGPT 自定义版…

线程池构造方法的认识

线程池中构造方法的认识 文章目录 线程池中构造方法的认识corePoolSize (核心线程数)maximumPoolSize&#xff08;最大线程数&#xff09;keepAliveTime(非核心线程的空闲超时时间)TimeUnitworkQueuethreadFactoryRejectedExecutionHandler拒绝策略 标准库中提供了一个ThreadPo…

智能优化算法应用:基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于北方苍鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.北方苍鹰算法4.实验参数设定5.算法结果6.…

matlab实践(十一):导弹追踪

1.题目 a9.94,x062.06 2.方程 我们有&#xff1a; ( d x d t ) 2 ( d y d t ) 2 w 2 (\frac{\mathrm d\mathrm x}{\mathrm d\mathrm t})^2(\frac{\mathrm d\mathrm y}{\mathrm d\mathrm t})^2\mathrm w^2 (dtdx​)2(dtdy​)2w2 还有导弹始终指向船 ( d x d t d y d t ) …

mingw下编译opencv4.5.2

初衷&#xff1a; 加载之前的模型没问题&#xff0c;但最近加载另一个模型时出现报错&#xff1a; OpenCV: terminate handler is called! The last OpenCV error is: OpenCV(4.1.0) Error: Assertion failed (nodesMapIt ! nodesMap.end()) in sortByExecutionOrder, file G…

Vue和jQuery:横向对比

聚沙成塔每天进步一点点 ⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vue 技能树参考资料专栏!创建这个专栏的初衷是为了帮助大家更好地应对 Vue.js 技能树的学习。每篇文章都致力于提供清晰、深入的参考资料,让你能够更轻松、更自信地理解和掌握 Vue.js 的核心概念和技…

java并发编程三 共享内存的问题和synchronized解决方案

文章目录 共享带来的问题synchronized 解决方案方法上的 synchronized 共享带来的问题 小故事 老王&#xff08;操作系统&#xff09;有一个功能强大的算盘&#xff08;CPU&#xff09;&#xff0c;现在想把它租出去&#xff0c;赚一点外快 小南、小女&#xff08;线程&…

数字人解决方案——ER-NeRF实时对话数字人模型推理部署带UI交互界面

简介 这个是一个使用ER-NeRF来实现实时对话数字人、口播数字人的整体架构&#xff0c;其中包括了大语言回答模型、语音合成、成生视频流、背景替换等功能&#xff0c;项目对显存的要求很高&#xff0c;想要达到实时推理的效果&#xff0c;建议显存在24G以上。 实时对话数字人 …

CycleGAN-两个领域非匹配图像的相互转换

1. CycleGAN的简介 pix2pix可以很好地处理匹配数据集图像转换&#xff0c;但是在很多情况下匹配数据集是没有的或者是很难收集到的&#xff0c;但是我们可以很容易的得到两个领域大量的非匹配数据。2017年有两篇非常相似的论文CycleGAN和DiscoGAN&#xff0c;提出了一种解决非匹…