【设计模式(11)】结构型模式之享元模式
个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道
如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充
前言
对于后端开发者而言,池化技术相当常见,比如线程池、数据库连接池、缓冲池,以及最最常见的Bean
池
对于Bean
池,在系统初始化的时候初始化装载Bean对象,系统中需要使用的时候直接调用Bean
池中的对象即可,而无需每次都去初始化一遍,以节省资源消耗
针对String
的性能优化,Java
引用了缓存池的概念,即创建String
类型数据的时候,会检查缓存池中是否有相同内容的String
类型对象,如果有会被直接引用,没有则会创建一个存入缓存池,从而避免重复创建String
对象的无谓资源消耗
Integer
型数据默认会有-128~127
的缓存池,以规避这些高频率出现的数据的重复创建过程,节省资源
享元(Flyweight)模式运用共享技术,通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
1.介绍
使用目的:运用共享技术来复用需要重复使用的资源
使用时机:系统中需要使用大量对象,且系统不依赖这些对象的身份,且这些对象可以分组,每组都可以用一个对象来代替
解决问题:系统中存在大量相同的对象,造成无谓的资源消耗,甚至造成内存溢出
实现方法:用 HashMap
存储这些对象,并使用唯一标识标记,对于不存在的对象创建并存入,已存在的则直接取出使用
应用实例:
Java
中的String
缓存- 池化技术,如线程池、连接池、bean池等
优点:减少了系统的资源消耗,降低了系统压力,提高执行效率
缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
注意事项:
- 注意划分外部状态和内部状态,否则容易引起线程安全问题
- 这些对象最好由工厂控制
2.结构
享元模式的主要角色有如下。
- 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口。
- 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
- 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
- 客户端(client)调用享元工厂(Flyweight Factory)角色中的方法
- 享元工厂通过
HashMap
结构持有具体享元(Concrete Flyweight)角色对象 - 具体享元角色则需要实现接口Flyweight
此图中未包括复杂数据的存取,请按照实际需求调整,实际上的数据肯定更加复杂
3.实现
定义抽象享元角色(Flyweight)
interface Flyweight {void operation(); }
定义具体享元(Concrete Flyweight)角色,实现接口Flyweight
class ConcreteFlyweight implements Flyweight {private String key;ConcreteFlyweight(String key) {this.key = key;System.out.println("具体享元" + key + "被创建!");}public void operation() {System.out.println("具体享元" + key + "被调用,");} }
定义享元工厂(Flyweight Factory)角色,管理Concrete Flyweight
class FlyweightFactory {private HashMap<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {Flyweight flyweight = (Flyweight) flyweights.get(key);if (flyweight != null) {// System.out.println("具体享元" + key + "已经存在,被成功获取!");} else {flyweight = new ConcreteFlyweight(key);flyweights.put(key, flyweight);}return flyweight;} }
完整代码
package com.company.test.flyweight;import java.util.HashMap;interface Flyweight {void operation();
}class ConcreteFlyweight implements Flyweight {private String key;ConcreteFlyweight(String key) {this.key = key;System.out.println("具体享元" + key + "被创建!");}public void operation() {System.out.println("具体享元" + key + "被调用,");}
}class FlyweightFactory {private HashMap<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String key) {Flyweight flyweight = (Flyweight) flyweights.get(key);if (flyweight != null) {// System.out.println("具体享元" + key + "已经存在,被成功获取!");} else {flyweight = new ConcreteFlyweight(key);flyweights.put(key, flyweight);}return flyweight;}
}public class FlyweightTest {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();System.out.println("------------------------------------------ 分别引用享元a,b ------------------------------------------");factory.getFlyweight("a").operation();factory.getFlyweight("b").operation();System.out.println("------------------------------------------ 分别引用享元a,b,c ------------------------------------------");factory.getFlyweight("a").operation();factory.getFlyweight("b").operation();factory.getFlyweight("c").operation();}
}
运行结果
- 第一轮调用享元a,b时,均需要创建对象再引用
- 第二轮调用享元a,b时不再创建,而是直接引用,但对于c依然需要创建后引用
- 同理,之后如果再次调用享元a,b,c也不需要创建,而是直接引用即可
4.模拟示例
为了更加贴近我们实际使用的情况,模拟一个下载管理器
临时手写的,肯定不够完整,仅写出其中主要逻辑,其中下载逻辑未补全
package com.company.test.flyweight;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** 任务接口*/
interface DownloadTask {void startDownload();void stopDownload() throws InterruptedException;
}/*** 公共任务虚拟类*/
abstract class CommonTask implements DownloadTask {protected String fileName;protected int progress;protected boolean isAlive;protected Thread thread;@Overridepublic void stopDownload() {//todo 仅做暂停任务,如果要删除任务,请先暂停,不然可能导致线程问题isAlive = false;System.out.println(new Date().toString() + ": " + fileName + " " + "downLoad stop!");}@Overridepublic void startDownload() {//已下载完成,不再继续下载if (progress >= 100) {//todo 这里仅做提示,实际应用中可以添加其他逻辑System.out.println(new Date().toString() + ": " + fileName + " " + "has been downloaded!");return;}System.out.println(new Date().toString() + ": " + fileName + " " + "downLoad start!");//正在下载中,不需要重建线程if (!isAlive) {isAlive = true;thread = createThread();thread.start();}}protected Thread createThread() {return new Thread(() -> {while (isAlive && progress < 100) {//todo 这里只做模拟进度,实际进度请自行扩展,不做赘述progress += 20;System.out.println(new Date().toString() + ": " + fileName + " " + progress + "%");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}if (progress >= 100) {isAlive = false;System.out.println(new Date().toString() + ": " + fileName + " " + "has bean downLoaded successfully!");}});}}class VideoTask extends CommonTask {//todo video类型的特有逻辑public VideoTask(String fileName) {this.fileName = fileName;this.progress = 0;this.isAlive = false;}
}class PicTask extends CommonTask {//todo picture类型的特有逻辑public PicTask(String fileName) {this.fileName = fileName;this.progress = 0;this.isAlive = false;}
}/*** 下载管理器*/
class DownloadManager {/*** 任务池*/Map<String, DownloadTask> tasks = new HashMap<>();/*** 获取任务,或者创建任务*/public DownloadTask getTask(String fileName) {DownloadTask task = tasks.get(fileName);if (task == null) {if (fileName.endsWith(".mp3")) {task = new VideoTask(fileName);} else {task = new PicTask(fileName);}tasks.put(fileName, task);}return task;}
}public class DownloadTest {public static void main(String[] args) throws InterruptedException {DownloadManager downloadManager = new DownloadManager();System.out.println("------------------------------------------ 第一轮测试 ------------------------------------------");//获取任务,并开始下载DownloadTask task1 = downloadManager.getTask("你的酒馆对我打了烊.mp3");task1.startDownload();//三秒后暂停下载Thread.sleep(3000);task1.stopDownload();System.out.println("------------------------------------------ 第二轮测试 ------------------------------------------");//三秒再次获取任务并下载Thread.sleep(3000);DownloadTask task2 = downloadManager.getTask("你的酒馆对我打了烊.mp3");task2.startDownload();//获取新任务并下载DownloadTask task3 = downloadManager.getTask("土拨鼠尖叫.jpg");task3.startDownload();System.out.println("------------------------------------------ 第三轮测试 ------------------------------------------");//三秒后重新开始两项任务Thread.sleep(3000);DownloadTask task4 = downloadManager.getTask("你的酒馆对我打了烊.mp3");task4.startDownload();DownloadTask task5 = downloadManager.getTask("土拨鼠尖叫.jpg");task5.startDownload();//维持主线程while (true) {}}
}
运行结果
- 第一轮测试中新建mp3下载,并在三秒后终止,结束时下载进度为60%
- 第二轮再次启动mp3的下载,并新建jpg下载
- 第三轮先等待三秒再启动下载
- mp3已下载完成,提示已下载
- jpg仍在下载中,则继续下载,直至下载完成
仅供参考池化逻辑,实际应用中的下载管理器比这要复杂多了,但依然需要池化管理
5.其他问题
5.1.为什么需要池化管理
节省系统资源,降低内存消耗,加快执行速度
比如上面示例中,不使用池化管理,则每次获取任务都需要重建,造成不必要的消耗,且会丢失已下载进度
作者:Echo_Ye
WX:Echo_YeZ
Email :echo_yezi@qq.com
个人站点:在搭了在搭了。。。(右键 - 新建文件夹)
5.2.如何共享
在合适的地方初始化享元工厂即可,如果想在多个类中公用,也可以将其丢进bean池,或者单例模式轻松解决,Android也可以使用服务
5.3.池化一定需要享元工厂吗?是否可以将池放在客户端里?
当然可以,实际上很多地方也是这么做的,但是封装性不够好,且不便于与其他客户端共享
通常如果客户端里定义享元工厂,会将类封装起来,不将池暴露出去,达到类似的效果
别的想起来再写吧。。。
后记
实际开发中经常使用到池化管理,算是常用的实战技巧了,享元模式只是更加规范化的整理出来而已
作者:Echo_Ye
WX:Echo_YeZ
Email :echo_yezi@qq.com
个人站点:在搭了在搭了。。。(右键 - 新建文件夹)
【设计模式(11)】结构型模式之享元模式相关推荐
- Java23种设计模式——11.结构型模式之享元模式
Java中除去有设计原则之外,还有23中设计模式. 这些模式都是前辈们一点一点积累下来,一直在改进,一直在优化的,而这些设计模式可以解决一些特定的问题. 并且在这些模式中,可以说是将语言的使用体现的淋 ...
- 设计模式GOF23之-------------------结构型模式(适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式)
一 结构型模式 二 适配器模式 下面我将用代码模拟键盘usb接口和ps/2的转接器 的适配器过程: 首先定义客户需求: package GOF23;public interface Target {v ...
- 设计模式(三)结构型模式(四)合成模式、享元模式
写在前面: 你好,欢迎你的阅读! 我热爱技术,热爱分享,热爱生活, 我始终相信:技术是开源的,知识是共享的! 博客里面的内容大部分均为原创,是自己日常的学习记录和总结,便于自己在后面的时间里回顾,当然 ...
- 6-设计模式之结构型模式(桥接模式、外观模式、组合模式、享元模式)
设计模式之结构型模式二(桥接模式.外观模式.组合模式.享元模式) 5.4 桥接模式 5.4.1 概述 5.4.2 结构 5.4.3 案例 5.4.4 使用场景 5.5 外观模式 5.5.1 概述 5. ...
- 带你认识4种设计模式:代理模式、装饰模式、外观模式和享元模式
摘要:本文我们主要介绍结构型模式中的代理模式.装饰模式.外观模式和享元模式. 本文分享自华为云社区<快来,这里有23种设计模式的Go语言实现(三)>,原文作者:元闰子 . 设计模式(Des ...
- 设计模式【10】-- 顺便看看享元模式
开局还是那种图,各位客官往下看- 享元模式是什么? 享元模式(FlyWeight),是结构型模式的一种,主要是为了减少创建对象的数量,减少内存占用以及提高性能.说到这里,不知道你是否会想到池技术,比如 ...
- 【设计模式2022】第十三章 享元模式
[设计模式2022]第十三章 享元模式 文章目录 [设计模式2022]第十三章 享元模式 一.概述 二.结构 三.实现 四.分析 1.优点 2.缺点 3.使用场景 五.Integer 包装类 一.概述 ...
- 结构模式--之--享元模式
享元模式是对象的结构模式,享元模式以共享的方式高效地支持大量的细粒度对象.享元对象能做到共享的关键区分内蕴状态和外蕴状态. 一个内蕴状态是存储在享元对象内部的,并且是不会随着环境改变而有所不同,因此, ...
- Java设计模式学习总结(13)——结构型模式之享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试 ...
最新文章
- mysql中的where,group by,having:
- MyISAM与InnoDB的索引实现
- Flume向HDFS写数据实例
- 5种流行的Web抓取Python库,你用过哪种?
- bash-高级编程--变量和参数介绍
- 深入学习SAP UI5框架代码系列之五:SAP UI5控件的实例数据修改和读取逻辑
- eclipse 新建java无scr_解决eclipse中没有js代码提示的问题
- 解释一下全连接层CNN中全连接层是什么样的
- 云原生应用Go语言:你还在考虑的时候,别人已经应用实践
- python读取大文件的某行_python 大文件以行为单位读取方式比对
- 用计算机按45乘5CE再按,2015年4月全国自学考试计算机应用基础真题
- android开发点击版本号多次无法进入开发者模式模式
- 大规模行人检索—PRCV2020竞赛发布
- 【DP专题】——洛谷P5144蜈蚣
- 2018杭州·云栖大会:一文直击地表最强黑科技
- 3.17 inappropriateintimacy (狎昵关系)
- 关于51单片机驱动DS18B20代码的感想
- 内行人看鸿蒙系统,如何看待华为终端2020年全线搭载鸿蒙系统?内行人“一语道破”...
- python dataset_数据管道Dataset
- MicroSoft Visual Studio 2013 社区版下载地址
热门文章
- 2.11 结构化程序设计思想
- 控制文本框只能输入数字字母和汉字
- 小小游戏S.漂亮的FLASH小小游戏S
- 浏览器notification调用系统通知
- i7 13700k和i7 12700k差距 i713700k和i712700k对比
- phpExcel导出excel加超级链接和图片的实例代码
- Mybatis————核心配置文件标签和动态常用的SQL标签
- 我的世界服务器修改物品模型,教程/修改实体模型 _ 《我的世界》中文Minecraft Wiki:最详细的官方我的世界百科...
- 计算机桌面运行慢,Win7电脑运行慢的解决方法
- 全球及中国加氢站行业十四五运营模式与项目可行性研究报告2022版