本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
定义
运用共享技术有效地支持大量细粒度的对象。
说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。
UML结构图
其中,Flyweight是抽象享元角色。它是产品的抽象类,同时定义出对象的外部状态和内部状态(外部状态及内部状态相关内容见后方)的接口或实现;ConcreteFlyweight是具体享元角色,是具体的产品类,实现抽象角色定义的业务;UnsharedConcreteFlyweight是不可共享的享元角色,一般不会出现在享元工厂中;FlyweightFactory是享元工厂,它用于构造一个池容器,同时提供从池中获得对象的方法。
举个例子:
// 所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。
abstract class Flyweight //要求享元角色必须接受外部状态
( //外部状态
protected val extrinsic: String
) {
//内部状态
var intrinsic: String? = null
//定义业务操作
abstract fun operate(extrinsic: Int)
} // 继承Flyweight超类或实现Flyweight接口,并为其内部状态增加存储空间。
class ConcreteFlyweight //接受外部状态
(extrinsic: String) : Flyweight(extrinsic) {
//根据外部状态进行逻辑处理
override fun operate(extrinsic: Int) {
println("具体Flyweight:$extrinsic")
}
} // 指那些不需要共享的Flyweight子类。
class UnsharedConcreteFlyweight(extrinsic: String) : Flyweight(extrinsic) {
override fun operate(extrinsic: Int) {
println("不共享的具体Flyweight:$extrinsic")
}
}
// 一个享元工厂,用来创建并管理Flyweight对象,主要是用来确保合理地共享Flyweight,
// 当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或创建一个实例。
object FlyweightFactory {
//定义一个池容器
private val pool = HashMap<String, Flyweight>()
//享元工厂
fun getFlyweight(extrinsic: String): Flyweight? {
var flyweight: Flyweight? = null
if (pool.containsKey(extrinsic)) { //池中有该对象
flyweight = pool[extrinsic]
print("已有 $extrinsic 直接从池中取---->")
} else {
//根据外部状态创建享元对象
flyweight = ConcreteFlyweight(extrinsic)
//放入池中
pool[extrinsic] = flyweight
print("创建 $extrinsic 并从池中取出---->")
}
return flyweight
}
}
fun main(){
var extrinsic = 22
val flyweightX = getFlyweight("X")
flyweightX!!.operate(++extrinsic)
val flyweightY = getFlyweight("Y")
flyweightY!!.operate(++extrinsic)
val flyweightZ = getFlyweight("Z")
flyweightZ!!.operate(++extrinsic)
val flyweightReX = getFlyweight("X")
flyweightReX!!.operate(++extrinsic)
val unsharedFlyweight: Flyweight = UnsharedConcreteFlyweight("X")
unsharedFlyweight.operate(++extrinsic)
}
复制代码
运行结果:
从这个结果我们可以看出来,第一次创建X、Y、Z时,都是先创建再从池中取出,而第二次创建X时,因为池中已经存在了,所以直接从池中取出,这就是享元模式。
享元模式的应用
1. 何时使用
- 系统中有大量对象时
- 这些对象消耗大量内存时
- 这些对象的状态大部分可以外部化时
2. 方法
- 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
3. 优点
- 大大减少了对象的创建,降低了程序内存的占用,提高效率
4. 缺点
- 提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变
5. 使用场景
- 系统中存在大量相似对象
- 需要缓冲池的场景
6. 应用实例
- String常量池
- 数据库连接池
7. 注意事项
- 注意划分内部状态和外部状态,否则可能会引起线程安全问题
- 这些类必须有一个工厂类加以控制