54 | 享元模式(上):如何利用享元模式优化文本编辑器的内存占用?
王争
该思维导图由 AI 生成,仅供参考
上一节课中,我们讲了组合模式。组合模式并不常用,主要用在数据能表示成树形结构、能通过树的遍历算法来解决的场景中。今天,我们再来学习一个不那么常用的模式,享元模式(Flyweight Design Pattern)。这也是我们要学习的最后一个结构型模式。
跟其他所有的设计模式类似,享元模式的原理和实现也非常简单。今天,我会通过棋牌游戏和文本编辑器两个实际的例子来讲解。除此之外,我还会讲到它跟单例、缓存、对象池的区别和联系。在下一节课中,我会带你剖析一下享元模式在 Java Integer、String 中的应用。
话不多说,让我们正式开始今天的学习吧!
享元模式原理与实现
所谓“享元”,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。
具体来讲,当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,起到节省内存的目的。实际上,不仅仅相同对象可以设计成享元,对于相似对象,我们也可以将这些对象中相同的部分(字段)提取出来,设计成享元,让这些大量相似对象引用这些享元。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
享元模式是一种在软件开发中常用的设计模式,本文通过棋牌游戏和文本编辑器两个实际例子详细介绍了享元模式的原理和实现。该模式的核心思想是复用对象,节省内存,适用于存在大量重复对象的场景。在棋牌游戏中,通过将棋子的属性设计成享元,实现了棋盘共享棋子信息,节省了内存。在文本编辑器中,通过将字体格式设计成享元,实现了不同文字共享使用相同的字体格式,进一步节省内存。文章通过实例代码展示了享元模式的简单实现,以及在棋牌游戏和文本编辑器中的应用。此外,文章还对享元模式与单例、缓存、对象池等概念进行了对比,强调了它们之间的区别。读者通过本文可以快速了解享元模式的原理、实现和在实际开发中的应用场景,为优化内存占用提供了有益的参考。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》,新⼈⾸单¥98
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(79)
- 最新
- 精选
- 天草二十六难道就我一个人有疑问?象棋不是总共32个棋子吗,争哥咋说30个享元对象?
作者回复: ������
2020-09-1197 - Laughing1. 第一个问题看需求,如果像象棋类的网格棋,可以考虑享元模式 2. map存储,减少迭代查找
作者回复: 嗯嗯 ������
2020-11-203 - SeamanW要不要把文字编辑器中的文字设计成享元呢? 正方:常用汉字也就3000多个,超过3000字原则上就需要 反方:设计成享元能节省多少空间呢?或者会不会更浪费空间? 哈哈😄
作者回复: 是个值得讨论的问题~
2020-07-1722 - Ken张云忠1.在棋牌游戏的例子中,有没有必要把 ChessPiecePosition 设计成享元呢? 没有必要,设计成享元模式主要是为了节省内存资源. ChessPiece中的positionX和positionY共占用8个字节,而把ChessPiecePosition设计成享元模式,ChessPiecePosition的引用在ChessPiece中也是占用8个字节,反而还需要额外的内存空间来存放棋盘中各个位置的对象,最终就得不偿失了. 当启用压缩指针时,ChessPiece对象占用(12+4+4+补4)24个字节, 当不启用压缩指针时,ChessPiece对象占用(16+4+补4+8)32个字节. 2.在文本编辑器的例子中,调用 CharacterStyleFactory 类的 getStyle() 方法,需要在 styles 数组中遍历查找,而遍历查找比较耗时,是否可以优化一下呢? 用map来存储数据CharacterStyle,重写CharacterStyle的hash方法,查找时就创建出新的对象来获取该hash值,用该hash值在map中查找是否存在,如果存在就直接返回,如果不存在就先添加到map中再返回.2020-03-0627190
- 韭菜河子补充一下,对于第二个问题,用LinkedHashMap容器并开启它的LRU策略来装CharacterStyle更好,因为根据一个使用者的习惯,常用的字体风格就是自己最近使用的。2020-03-08143
- Xion1. 没有必要,每局游戏的棋子位置不是完全相同的数据,这取决于用户的输入,随着时间的推移会不断地变化。而使用享元模式保存的数据,应当是那些不变的,会被大量对象复用的数据。 2.可以考虑使用哈希表保存文本格式,用多出来的一点点空间占用换取O(1)的查询效率。2020-03-06321
- 前端西瓜哥1. 没有必要。棋盘上位置的点集是有限的,是可以设计成享元的。但我们只需要存两个很小的整数,用上享元代码就会变复杂,另外指针也要存储空间。设计成享元可以,但没有必要。 2. 用哈希表提高查询速度:将 font, size, style 连接为字符串(比如 'yahei-12-123456')作为 hash 表的 key。2020-03-07318
- Wh1小争哥你好,采用享元模式重构的代码中,CharacterStyleFactory的getStyle()函数是不是设计的有问题。无论styles中是否存在已经创建好的享元对象,都会新建一个CharacterStyle对象。照这么看,styles岂不是根本就没有存在的必要了。 我认为代码应该改为如下: public static CharacterStyle getStyle(Font font, int size, int colorRGB) { //遍历styles, 如果styles中有相同对象, 则返回 for (CharacterStyle style : styles) { if (style.equals(font, size, colorRGB)) { return style; } } CharacterStyle newStyle = new CharacterStyle(font, size, colorRGB); styles.add(newStyle); return newStyle; }2020-03-06912
- Wh1在避免创建CharacterStyle对象同时,以O(1)的时间复杂度判断CharacterStyle是否已经被创建,代码如下: public class CharacterStyleFactory { private static final Map<Integer, CharacterStyle> styles = new HashMap<>(); public static CharacterStyle getStyle(Font font, int size, int colorRGB) { //key = font的哈希值 + size + colorRGB 以保证哈希值唯一性, 同时也避免了重复创建CharacterStyle的开销 int key = font.hashCode() + size + colorRGB; if (styles.containsKey(key)) { return styles.get(key); } CharacterStyle newStyle = new CharacterStyle(font, size, colorRGB); styles.put(key, newStyle); return newStyle; } }2020-03-06211
- Jackey前面看的时候就在想感觉有点像连接池,当看到一个“共享使用”,一个“重复使用”时真是有种恍然大悟的感觉2020-03-0648
收起评论