一.概述
享元模式(Flyweight Pattern)
,运用共享技术有效的支持大量的细粒度对象。换句话说,使用享元模式对相同或者类似的对象进行复用,以此来减少少相同或者类似的对象的创建数量,从而减少内存占用,提高性能。
享元模式属于结构型模式。
在具体了解前先介绍两个名词:
- 内部状态:对象内部共享的内容,不随环境的改变而改变。
- 外部状态:相对内部状态,不可以被共享,而且会随环境改变而变化的状态就是外部状态。
有些时候又把享元模式分成单纯的享元模式和复合享元模式,下面分别介绍。
二.单纯享元模式
单纯享元模式,也就是说所有的具体享元类都是可以被共享的,不存在不可以共享的具体享元类。
1. UML类图解析
单纯享元模式的UML类图如下:
- FlyWeight:享元类的接口,接口中operation方法中的extrinsicState为外部状态。
- ConcreteFlyWeight:具体的享元类,实现接口FlyWeight,存储内部状态intrinsicState。
- FlyWeightFactory:享元工厂,用来创建和管理享元对象,其主要思想就是用一个Map来保存已经创建的对象实例。它主要是用来确保合理的共享FlyWeight对象,当用户请求一个FlyWeight对象时(getFlyWeight()方法),工厂提供一个已经存在的FlyWeight实例,如果不存在则创建一个返回。
享元工厂最好使用单例模式实现。
2. 代码实现
享元类接口——FlyWeight
1 | package io.github.brightloong.lab.design.flyweight; |
具体享元实现——ConcreteFlyWeight
1 | package io.github.brightloong.lab.design.flyweight; |
享元工厂——FlyWeightFactory
1 | package io.github.brightloong.lab.design.flyweight; |
客户端调用和输出
1 | package io.github.brightloong.lab.design.flyweight; |
输出结果如下:1
2
3
4
5true
内部状态:b
外部状态:hello
内部状态:b
外部状态:hi
三. 复合享元模式
复合享元模式,将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。
复合享元模式中,组成复合享元对象的每个单纯享元对象拥有自己的内部状态,而每个单纯享元对象的外部状态都和复合享元对象的外部状态相同。所以复合享元模式可以对多个单纯享元对象设置相同的外部状态, 这也是复合享元模式的应用场景。
1. UML类图解析
复合享元模式的UML类图如下:
- FlyWeight:享元类的接口,接口中operation方法中的extrinsicState为外部状态,是单纯享元类和复合享元类的共同接口。
- ConcreteFlyWeight:具体的享元类,实现接口FlyWeight,存储内部状态intrinsicState。
- CompositeConcreteFlyweight:复合享元类,实现FlyWeight接口,同时使用Map来保存单纯享元对象,并提供add()和remove()方法来增加或者删除单纯享元对象。
- FlyWeightFactory:享元工厂,用来创建和管理享元对象,其主要思想就是用一个Map来保存已经创建的对象实例。它主要是用来确保合理的共享FlyWeight对象。
- getFlyWeight(state:String):单纯享元工厂方法,当用户请求一个FlyWeight对象时,工厂提供一个已经存在的FlyWeight实例,如果不存在则创建一个返回(直接返回单纯享元对象)。
- getFlyWeight(states:List
):复合享元工厂方法,根据提供的states:List ,使用add()方法,将每个state对应的单纯享元对象添加到复合享元对象(CompositeConcreteFlyweight)的flyWeights中,并且这些单纯享元对象是从FlyWeightFactory的flyWeights中获取的(如果不存在则添加并返回),因此这些单纯享元对象是可以共享的。
2. 代码实现
接口和单纯享元类的代码在上面已经列出了,下面是其他相关代码。
复合享元类——CompositeConcreteFlyweight
1 | package io.github.brightloong.lab.design.flyweight; |
修改后的享元工厂——FlyWeightFactory
1 | package io.github.brightloong.lab.design.flyweight; |
客户端调用和输出
1 | package io.github.brightloong.lab.design.flyweight; |
输出结果如下:1
2
3
4
5
6
7false
内部状态:a
外部状态:hello
内部状态:b
外部状态:hello
内部状态:c
外部状态:hello
四. 总结
使用场景
- 系统中有大量相同或者相似的对象,因此而造成了系统存储开销。
- 对象的大多数状态是外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。也就是说可以将外部状态使用传入对象的方式来处理。
优点
- 实现了对象的共享
- 使用享元模式减少对象的创建,减少了对内存的使用。
缺点
- 使用享元模式,需要分离出内部状态和外部状态,使得系统更加复杂。
多说一句
Java中String就是对享元模式的应用,如下代码会输出true。
1 | String a = "Hello Word"; |