【深入理解设计模式】享元设计模式

享元设计模式

在这里插入图片描述

概述

享元设计模式(Flyweight Design Pattern)是一种用于性能优化的设计模式,它通过共享尽可能多的相似对象来减少对象的创建,从而降低内存使用和提高性能。享元模式的核心思想是将对象的共享部分提取出来,使得多个对象可以共享同一个享元对象,从而减少对象的创建。

定义:

​ 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。

结构

享元(Flyweight )模式中存在以下两种状态:

  1. 内部状态,即不会随着环境的改变而改变的可共享部分。
  2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。

享元模式的结构如下:

  1. 抽象享元类(Flyweight):定义一个抽象类,包含一个享元对象的属性以及一个构造函数。

  2. 具体享元类(ConcreteFlyweight):实现抽象享元类,定义具体的享元对象。

  3. 享元工厂类(FlyweightFactory):负责创建和管理享元对象。享元工厂类会缓存已经创建的享元对象,当需要获取享元对象时,首先从缓存中查找,如果找不到,则创建一个新的享元对象并将其添加到缓存中。

享元模式的实现示例:

以下是一个简单的享元模式实现示例:

// 抽象享元类
public abstract class Flyweight {public abstract void operation();
}// 具体享元类
public class ConcreteFlyweight extends Flyweight {private String state;public ConcreteFlyweight(String state) {this.state = state;}@Overridepublic void operation() {System.out.println("ConcreteFlyweight: " + state);}
}// 享元工厂类
public class FlyweightFactory {private Map<String, Flyweight> flyweights = new HashMap<>();public Flyweight getFlyweight(String state) {if (!flyweights.containsKey(state)) {flyweights.put(state, new ConcreteFlyweight(state));}return flyweights.get(state);}
}// 客户端
public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("state1");Flyweight flyweight2 = factory.getFlyweight("state2");Flyweight flyweight3 = factory.getFlyweight("state1");flyweight1.operation();flyweight2.operation();flyweight3.operation();}
}

在这个示例中,我们定义了一个抽象享元类Flyweight和一个具体享元类ConcreteFlyweightFlyweightFactory类负责创建和管理享元对象,它使用一个HashMap来缓存已经创建的享元对象。客户端通过FlyweightFactory类来获取享元对象,并使用它们进行操作。

示例二:

/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 享元设计模式 - 抽象享元类*/
public abstract class AbstractBox {public abstract String getSharp();public void display(String color) {System.out.println("形状:" + this.getSharp() + ",颜色:" + color);}
}/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 具体享元类 - I图形*/
public class IBox extends AbstractBox {@Overridepublic String getSharp() {return "I";}
}/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 具体享元类 - L图形*/
public class LBox extends AbstractBox {@Overridepublic String getSharp() {return "L";}
}/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 具体享元类 - O图形*/
public class OBox extends AbstractBox {@Overridepublic String getSharp() {return "O";}
}
/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 图形工厂类 - 享元工厂类 【单例实现】*/
public class BoxFactory {private final Map<String, AbstractBox> map;private static final BoxFactory factory = new BoxFactory();private BoxFactory() {map = new HashMap<>();map.put("I", new IBox());map.put("O", new OBox());map.put("L", new LBox());}public static BoxFactory getInstance() {return factory;}public AbstractBox createBox(String name) {return map.get(name);}
}
/*** @author OldGj 2024/02/29* @version v1.0* @apiNote 客户端 - 测试类*/
public class Client {public static void main(String[] args) {BoxFactory factory = BoxFactory.getInstance();AbstractBox LBox = factory.createBox("L");LBox.display("红色"); // 外部状态AbstractBox oBox = factory.createBox("O");oBox.display("黑色"); // 外部状态AbstractBox IBox = factory.createBox("I");IBox.display("白色"); // 外部状态AbstractBox IBox2 = factory.createBox("I");IBox2.display("蓝色"); // 外部状态System.out.println("是否共享同一个对象:" + (IBox == IBox2));}
}

在这里插入图片描述

优缺点:

享元模式的优点:

  1. 减少对象创建:享元模式通过共享相似对象,减少了对象的创建,从而降低了内存使用和提高了性能。

  2. 提高性能:享元模式通过减少对象创建,提高了性能。

  3. 方便管理:享元模式将对象的共享部分提取出来,使得对象可以轻松地管理和维护。

享元模式的缺点:

  1. 享元对象共享:享元对象共享可能会导致一些问题,例如线程安全问题。享元模式要求享元对象是线程安全的,这可能会导致性能下降。

  2. 享元对象状态:享元对象的状态可能会影响其他使用相同享元对象的客户端。享元模式要求享元对象的状态是独立的,这可能会导致享元对象的状态难以维护。

  3. 享元对象生命周期:享元对象的生命周期可能会比客户端使用的时间长,这可能会导致内存泄漏。享元模式要求享元对象具有较长的生命周期,这可能会导致内存使用增加。

使用场景:

享元模式通常用于以下场景:

  1. 对象数量多且相似:当对象数量多且相似时,享元模式可以显著减少对象的创建,提高性能。

  2. 对象共享属性多:当对象共享属性多时,享元模式可以减少内存使用,提高性能。

  3. 对象创建开销大:当对象创建开销大时,享元模式可以减少对象创建的开销,提高性能。

JDK源码解析

Integer类使用了享元模式。我们先看下面的例子:

public class Demo {public static void main(String[] args) {Integer i1 = 127;Integer i2 = 127;System.out.println("i1和i2对象是否是同一个对象?" + (i1 == i2));Integer i3 = 128;Integer i4 = 128;System.out.println("i3和i4对象是否是同一个对象?" + (i3 == i4));}
}

运行上面代码,结果如下:

为什么第一个输出语句输出的是true,第二个输出语句输出的是false?通过反编译软件进行反编译,代码如下:

public class Demo {public static void main(String[] args) {Integer i1 = Integer.valueOf((int)127);Integer i2 Integer.valueOf((int)127);System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());Integer i3 = Integer.valueOf((int)128);Integer i4 = Integer.valueOf((int)128);System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());}
}

上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的是 valueOf() ,所以只需要看该方法即可

public final class Integer extends Number implements Comparable<Integer> {public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {int h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
}

可以看到 Integer 默认先创建并缓存 -128 ~ 127 之间数的 Integer 对象,当调用 valueOf 时如果参数在 -128 ~ 127 之间则计算下标并从缓存中返回,否则创建一个新的 Integer 对象。

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

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

相关文章

人机交互中的定向、定性、定量

在人机交互中&#xff0c;定向、定性和定量分析都扮演着重要的角色&#xff0c;帮助设计师更好地理解用户需求、优化系统设计&#xff0c;并评估系统的性能和效果。这种综合的分析方法有助于打造更符合用户期望、更高效的人机交互系统。 在人机交互中&#xff0c;定向&#xff…

实用干货:分享4个冷门但非常实用的HTML属性

大家好&#xff0c;我是大澈&#xff01; 本文约1100字&#xff0c;整篇阅读大约需要2分钟。 关注微信公众号&#xff1a;“程序员大澈”&#xff0c;免费加入问答群&#xff0c;一起交流技术难题与未来&#xff01; 现在关注公众号&#xff0c;免费送你 ”前后端入行大礼包…

炉石传说(ccf201609-3)解题思路

题目 * 玩家会控制一些角色&#xff0c;每个角色有自己的生命值和攻击力。当生命值小于等于 0 时&#xff0c;该角色死亡。角色分为英雄和随从。   * 玩家各控制一个英雄&#xff0c;游戏开始时&#xff0c;英雄的生命值为 30&#xff0c;攻击力为 0。当英雄死亡时&#xff…

软件设计师软考题目解析23 --每日五题

想说的话&#xff1a;要准备软考了。0.0&#xff0c;其实我是不想考的&#xff0c;但是吧&#xff0c;由于本人已经学完所有知识了&#xff0c;只是被学校的课程给锁在那里了&#xff0c;不然早找工作去了。寻思着反正也无聊&#xff0c;就考个证玩玩。 本人github地址&#xf…

TensorRT是什么,有什么作用,如何使用

TensorRT 是由 NVIDIA 提供的一个高性能深度学习推理&#xff08;inference&#xff09;引擎。它专为生产环境中的部署而设计&#xff0c;用于提高在 NVIDIA GPU 上运行的深度学习模型的推理速度和效率。以下是关于 TensorRT 的详细介绍&#xff1a; TensorRT 是 NVIDIA 推出的…

Hive中增量插入的处理

增量数据采集&#xff0c;目前实现的方式是hive中按某个字段创建分区表&#xff0c; insert override的时候where语句带上对应的增量过滤条件。 我一般选取日期字段ETL_DATE。 hive建立分区表&#xff0c;hql如下&#xff1a; CREATE TABLE IF NOT EXISTS product_sell( cate…

抖店怎么运营?学会这个,玩赚整个抖店市场!

我是电商珠珠 我做电商已经有五年的时间了&#xff0c;做抖店也3年多了&#xff0c;期间还带着学生一起做店。 今天就来给你们讲讲店铺的运营流程&#xff0c;你只要按照这个流程去做店&#xff0c;理解了其中的精髓&#xff0c;就会有明显的效果。 一、类目 抖店运营的第一…

freeRTOS20240308

1.总结任务的调度算法&#xff0c;把实现代码再写一下 2.总结任务的状态以及是怎么样进行转换的

Java集合面试题(day 02)

&#x1f4d1;前言 本文主要是【JAVA】——Java集合面试题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&am…

容器: string

引言: 为什么要有string类型, 就使用字符数组表示字符串不行吗? 原因: 使用字符数组描述文本信息, 无法确定开多大空间, 开多了浪费,开少了不够用使用string封装: 扩容机制:减少了空间的浪费各种接口:方便修改等操作 string的使用 容量相关 size:获取字符个数,不包含\0 (C语言…

从huggingface下载模型像本地加载但是UnicodeDecodeError

我自己是在Linux下出现了这个问题 原文&#xff1a;https://github.com/huggingface/transformers/issues/13674 The path for the AutoModel should be to a directory pointing to a pytorch_model.bin and to a config.json. Since you’re pointing to the .bin file dire…

探究java final、finally、finalize的异同

探究final、finally、finalize的异同 在Java编程语言中&#xff0c;final、finally和finalize是三个看似相似但实际上用途迥异的关键词。它们各自在Java的不同场景中扮演着重要角色。本文旨在深入探讨这三个关键词的含义、用法以及它们之间的区别。 1. final final是一个Jav…

无限debugger的几种处理方式

不少网站会在代码中加入‘debugger’&#xff0c;使你F12时一直卡在debugger&#xff0c;这种措施会让新手朋友束手无策。 js中创建debugger的方式有很多&#xff0c;基础的形式有&#xff1a; ①直接创建debugger debugger; ②通过eval创建debugger&#xff08;在虚拟机中…

ERD Online 快速启动指南:代码下载到首次运行的全流程攻略 ️

&#x1f680; 一、代码下载 ERD online前端代码正常拉取即可&#x1f44c; 后端代码含有子模块&#xff0c;拉取命令如下&#xff1a; git clone --recurse-submodules https://github.com/www-zerocode-net-cn/martin-framework.git &#x1f6e0;️ 二、代码构建 &#x1f3…

PROTEUS可以在单片机设计时帮助你做什么

引言 在单片机&#xff08;MCU&#xff09;设计过程中&#xff0c;验证和调试是非常重要的步骤。然而&#xff0c;使用实际硬件进行验证和调试需要大量的时间和成本。这时&#xff0c;PROTEUS作为一款强大的电子设计自动化软件&#xff0c;可以极大地提高设计效率&#xff0c;…

算法二刷day3

203.移除链表元素 class Solution { public:ListNode* removeElements(ListNode* head, int val) {ListNode *dummyHead new ListNode(0);dummyHead->next head;ListNode *cur dummyHead;while (cur->next ! nullptr) {if (cur->next->val val) {ListNode *tm…

安全防御-第七次

在FW5和FW6之间建立一条IPSEC通道保证10.0.2.0/24网段可以正常访问到192.168.1.0/24 NAT&#xff1a; 安全策略&#xff1a; NAT: 安全策略&#xff1a; 修改服务器映射&#xff1a; 配置IPSEC&#xff1a;

物联网的商业模式洞察

大约在十年前&#xff08;2014年11月&#xff09;&#xff0c;全球知名管理思想家、哈佛商学院教授迈克尔波特与PTC前首席执行官吉姆赫普尔曼&#xff0c;在《哈佛商业评论》上联合撰写了一篇备受赞誉的文章&#xff0c;题为《智能互联产品如何改变竞争》。在这篇文章中&#x…

零基础,学6个月嵌入式,能找到工作吗?

今天看到一个老铁问&#xff0c;他报了个班&#xff0c;学6个月&#xff0c;学完能找到工作吗&#xff1f; 我看了下他的学习内容&#xff0c;包含C语言、数据结构、系统编程、网络编程、STM32、RTOS、物联网通讯协议、Linux内核驱动&#xff0c;这是大纲&#xff0c;细节的课程…

前端算法之插入排序

3、插入排序&#xff08;Insertion Sort&#xff09; 插入排序&#xff08;Insertion-Sort&#xff09;的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入…