个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充

前言

对于后端开发者而言,池化技术相当常见,比如线程池、数据库连接池、缓冲池,以及最最常见的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.实现

  1. 定义抽象享元角色(Flyweight)

    interface Flyweight {void operation();
    }
    
  2. 定义具体享元(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 + "被调用,");}
    }
    
  3. 定义享元工厂(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)】结构型模式之享元模式相关推荐

  1. Java23种设计模式——11.结构型模式之享元模式

    Java中除去有设计原则之外,还有23中设计模式. 这些模式都是前辈们一点一点积累下来,一直在改进,一直在优化的,而这些设计模式可以解决一些特定的问题. 并且在这些模式中,可以说是将语言的使用体现的淋 ...

  2. 设计模式GOF23之-------------------结构型模式(适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式)

    一 结构型模式 二 适配器模式 下面我将用代码模拟键盘usb接口和ps/2的转接器 的适配器过程: 首先定义客户需求: package GOF23;public interface Target {v ...

  3. 设计模式(三)结构型模式(四)合成模式、享元模式

    写在前面: 你好,欢迎你的阅读! 我热爱技术,热爱分享,热爱生活, 我始终相信:技术是开源的,知识是共享的! 博客里面的内容大部分均为原创,是自己日常的学习记录和总结,便于自己在后面的时间里回顾,当然 ...

  4. 6-设计模式之结构型模式(桥接模式、外观模式、组合模式、享元模式)

    设计模式之结构型模式二(桥接模式.外观模式.组合模式.享元模式) 5.4 桥接模式 5.4.1 概述 5.4.2 结构 5.4.3 案例 5.4.4 使用场景 5.5 外观模式 5.5.1 概述 5. ...

  5. 带你认识4种设计模式:代理模式、装饰模式、外观模式和享元模式

    摘要:本文我们主要介绍结构型模式中的代理模式.装饰模式.外观模式和享元模式. 本文分享自华为云社区<快来,这里有23种设计模式的Go语言实现(三)>,原文作者:元闰子 . 设计模式(Des ...

  6. 设计模式【10】-- 顺便看看享元模式

    开局还是那种图,各位客官往下看- 享元模式是什么? 享元模式(FlyWeight),是结构型模式的一种,主要是为了减少创建对象的数量,减少内存占用以及提高性能.说到这里,不知道你是否会想到池技术,比如 ...

  7. 【设计模式2022】第十三章 享元模式

    [设计模式2022]第十三章 享元模式 文章目录 [设计模式2022]第十三章 享元模式 一.概述 二.结构 三.实现 四.分析 1.优点 2.缺点 3.使用场景 五.Integer 包装类 一.概述 ...

  8. 结构模式--之--享元模式

    享元模式是对象的结构模式,享元模式以共享的方式高效地支持大量的细粒度对象.享元对象能做到共享的关键区分内蕴状态和外蕴状态. 一个内蕴状态是存储在享元对象内部的,并且是不会随着环境改变而有所不同,因此, ...

  9. Java设计模式学习总结(13)——结构型模式之享元模式

    享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试 ...

最新文章

  1. mysql中的where,group by,having:
  2. MyISAM与InnoDB的索引实现
  3. Flume向HDFS写数据实例
  4. 5种流行的Web抓取Python库,你用过哪种?
  5. bash-高级编程--变量和参数介绍
  6. 深入学习SAP UI5框架代码系列之五:SAP UI5控件的实例数据修改和读取逻辑
  7. eclipse 新建java无scr_解决eclipse中没有js代码提示的问题
  8. 解释一下全连接层CNN中全连接层是什么样的
  9. 云原生应用Go语言:你还在考虑的时候,别人已经应用实践
  10. python读取大文件的某行_python 大文件以行为单位读取方式比对
  11. 用计算机按45乘5CE再按,2015年4月全国自学考试计算机应用基础真题
  12. android开发点击版本号多次无法进入开发者模式模式
  13. 大规模行人检索—PRCV2020竞赛发布
  14. 【DP专题】——洛谷P5144蜈蚣
  15. 2018杭州·云栖大会:一文直击地表最强黑科技
  16. 3.17 inappropriateintimacy (狎昵关系)
  17. 关于51单片机驱动DS18B20代码的感想
  18. 内行人看鸿蒙系统,如何看待华为终端2020年全线搭载鸿蒙系统?内行人“一语道破”...
  19. python dataset_数据管道Dataset
  20. MicroSoft Visual Studio 2013 社区版下载地址

热门文章

  1. 2.11 结构化程序设计思想
  2. 控制文本框只能输入数字字母和汉字
  3. 小小游戏S.漂亮的FLASH小小游戏S
  4. 浏览器notification调用系统通知
  5. i7 13700k和i7 12700k差距 i713700k和i712700k对比
  6. phpExcel导出excel加超级链接和图片的实例代码
  7. Mybatis————核心配置文件标签和动态常用的SQL标签
  8. 我的世界服务器修改物品模型,教程/修改实体模型 _ 《我的世界》中文Minecraft Wiki:最详细的官方我的世界百科...
  9. 计算机桌面运行慢,Win7电脑运行慢的解决方法
  10. 全球及中国加氢站行业十四五运营模式与项目可行性研究报告2022版