首先我们需要简单了解一下什么是享元模式。享元模式(Flyweight Pattern):主要用于减少创建对象的数量,以减少内存占用和提高性能。享元模式的重点就在这个享字,通过一些共享技术来减少对象的创建,实际上Java中String值的存储,Volley中的ByteArrayPool
也使用到了享元模式,这对提高性能很有用。
感觉目前大部分的缓存机制都会用到享元模式。
享元模式在一般情况下可以分成四个部分,也可以大体分为两部分,分别是享元工厂类
,享元(抽象)类
。如果学习过池有关的概念的话,我们就可以把享元工厂类
看做是一个池(Pool),享元类
看做是池中的数据。
标准享元模式的结构图:
可以看到除了共享的享元类之外还有一类不参与共享的享元类,因为享元模式只是使共享成为可能而不强制共享。
享元模式工作的流程和缓存机制很像(或许应该说缓存机制和它很像吗),当我们需要获取一个享元对象时享元工厂会检查其存储空间中是否已经有所需要的享元实例,如果有就将它直接返回,如果没有就创建一个并加入到享元工厂的存储空间中去。
首先我们直接定义一个享元类:
class flyweight(val id:Int) {
var message = "empty body"
fun execute(){
println(message)
}
}
这里我们就不定义接口什么的了,直接创建一个简单的例子。这是一个享元类,它有两个参数,分别是id和message。id值是每个享元类唯一确定的一个值,相当于数据库中的主键,我们想要通过这个值来判断这个享元类是否是我们想要的实例。message对象和享元类的具体执行有关。
接着我们定义一个工厂类:
class flyweightFactory{
val Pool = HashMap<Int,flyweight>()
fun getFlyweight(id: Int,msg:String):flyweight{
if (!Pool.containsKey(id)){ //如果没有找到对应的实例就新创建一个
val newInstance = flyweight(id)
Pool.put(id,newInstance)
}
return Pool.get(id)?.apply {
message = msg
}!!
}
}
这个类的逻辑很简单,它内部有一个Pool
变量来存储享元对象。还有一个getFlyweight
方法用于获取它内部管理的享元对象,该方法有两个参数,第一个参数即为我们想要获得的享元实例的id,这是用于判断我们想要的享元实例是否存在,第二个参数将会被赋予给最后返回的享元对象的message中,用来影响享元对象的execute
方法的执行结果。
内部状态和外部状态的概念干讲比较抽象,所以我们给完上面的简单例子之后结合来讲。首先我们联系享元模式的使用场景,其中之一就是拥有众多重复内容的对象,比如说一个ppt模板,对于ppt来说可能大部分的结构都是一致的,不变的只有上面显示的文字。这种情况下如果把ppt抽象成一个类的话,我们就可以共享它的模板,这个模板属于这个类内部的,我们称之为内部状态
,一般内部状态是不会被外部调用给更改的。而ppt需要显示的文字就属于是外部状态
,它取决于外部调用传入的参数。
我们可以对照上面的例子,其中显然flyweight类的id参数为内部参数,因为它一旦形成是不随外部传入的参数而改变的,这也可以看做是我们共享的东西。再抽象一点,这个execute方法也是内部状态,因为它的逻辑也不随外部参数而改变。而其中哪个message参数就是外部状态,因为它每次都会随着外部传入的参数而改变。