今天我们来学习最后一个设计模式:享元模式。
相对来说,享元模式的原理和实现也比较简单,并且在实际的项目开发中也不怎么常用。
概述
享元模式:(Flyweight Design Pattern)运用共享技术有效的支持大量细粒度
的对象。
所谓享元
,顾名思义就是被共享的单元。享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。
当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用。这样可以减少内存中对象的数量,起到节省内存的目的。
何时使用:
- 系统中有大量对象。
- 这些对象消耗大量内存。
- 这些对象的状态大部分可以外部化。
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 系统不依赖于这些对象身份,这些对象是不可分辨的。
UML 类图:
角色组成:
- 抽象享元(Flyweight)角色: 是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
- 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
- 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
- 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
实例解析
假设我们要开发一个象棋
游戏。一个游戏厅中有成千上万个房间
,每个房间对应一个棋局。棋局要保存每个棋子的数据:棋子类型
、棋子颜色
、棋子在棋局中的位置
。利用这些数据,我们就能显示一个完整的棋局给玩家。
此时,我们就可以使用享元模式来实现。具体代码如下:
享元类 ChessPieceUnit.java
1 | java复制代码public class ChessPieceUnit { |
棋子 ChessPiece.java
1 | java复制代码public class ChessPiece { |
享元工厂 ChessPieceUnitFactory.java
1 | arduino复制代码public class ChessPieceUnitFactory { |
棋局 ChessBoard.java
1 | java复制代码public class ChessBoard { |
总结
优缺点
- 优点: 大大减少对象的创建,降低系统的内存,使效率提高。
- 缺点: 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
应用
- String 常量池、数据库连接池、缓冲池等等。
- Java Integer 类中、String 类的
字符串常量池
。
实际上,享元模式对 JVM 的垃圾回收并不友好。因为享元工厂类一直保存了对享元对象的引用,这就导致享元对象在没有任何代码使用的情况下,也并不会被 JVM 垃圾回收机制自动回收掉。因此,在某些情况下,如果对象的生命周期很短,也不会被密集使用,利用享元模式反倒可能会浪费更多的内存。所以,除非经过线上验证,利用享元模式真的可以大大节省内存,否则,就不要过度使用这个模式,为了一点点内存的节省而引入一个复杂的设计模式,得不偿失。
本文转载自: 掘金