十九 结构型-享元模式(Flyweight)

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

image.png

定义

运用共享技术有效地支持大量细粒度的对象。

说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。

UML结构图

image.png

其中,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)
}
复制代码

运行结果:

image.png 从这个结果我们可以看出来,第一次创建X、Y、Z时,都是先创建再从池中取出,而第二次创建X时,因为池中已经存在了,所以直接从池中取出,这就是享元模式。

享元模式的应用

1. 何时使用

  • 系统中有大量对象时
  • 这些对象消耗大量内存时
  • 这些对象的状态大部分可以外部化时

2. 方法

  • 用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储

3. 优点

  • 大大减少了对象的创建,降低了程序内存的占用,提高效率

4. 缺点

  • 提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变

5. 使用场景

  • 系统中存在大量相似对象
  • 需要缓冲池的场景

6. 应用实例

  • String常量池
  • 数据库连接池

7. 注意事项

  • 注意划分内部状态和外部状态,否则可能会引起线程安全问题
  • 这些类必须有一个工厂类加以控制

猜你喜欢

转载自juejin.im/post/7016291736637046797
今日推荐