享元模式(Flyweight Pattern)

意图

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

使用场景

对那些通常因为数量太大而难以用对象来表示的概念或实体进行建模

  • 一个程序使用了大量相似甚至相同的对象。
  • 使用大量的对象,造成很大的存储开销。
  • 对象的大多数状态都可变为非本质状态(Extrinsic State),既可以由外部传入的状态。
  • 如果删除对象的非本质状态(Extrinsic State),那么可以用相对较少的共享对象取代很多组对象。
  • 应用程序不依赖于该类对象的标识。由于Flyweight对象将被共享,标识测试将返回真值。例如equals方法都将返回true。

模块

  • Flyweight:描述接口,提供本质状态(Intrinsic State)相关的数据和赋值非本质状态(Extrinsic State)。

  • ConcreteFlyweight:实现Flyweight接口,并为本质状态(Intrinsic State)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是固有的的;即它必须独立于ConcreteFlyweight对象的场景。

  • UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点(Row和Column就是这样) 。

  • FlyweightFactory:创建并管理Flyweight对象。确保合理地共享Flyweight对象。当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话) 。

  • Client:维持一个对Flyweight的引用。计算或存储一个(多个)Flyweight的外部状态。

享元模式(Flyweight Pattern)

优点

  1. 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
  2. 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  1. 使得系统变得复杂,需要对原有类进行分析,分离出内部状态和外部状态,提高的类的复杂度,这使得程序的逻辑复杂化。
  2. 使用工厂模式维护享元对象时,必然带来计算应用技术和何时垃圾回收的问题。不过往往共享对象数量特别的小,所以这个问题可以被忽略。
  3. 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

与其他模式的配合

  • 组合模式:Flyweight模式通常和组合模式结合起来,用共享叶结点的有向无环图实现一个逻辑上的层次结构。
  • 状态模式:最好用Flyweight实现状态模式对象。
  • 策略模式:最好用Flyweight实现策略模式对象。

优秀的应用

Java标准类库中的Integral类

Java标准类库中的String类

思考

本质状态(Intrinsic State)与非本质状态(Extrinsic State):说起本质与非本质往往是个十分玄学的概念,但是享元模式使用的大前提下,我认为在那数量庞大的相似的对象中,相似的属性该应该就能被称之为这一类对象的“本质”状态,其他的各个对象不同的状态则被称之为“非本质”状态。

与单例模式的区别:单例模式是在类级别上实现共享数据,享元模式是在对象级别上实现共享数据。单例模式中一个类就只能有一个实例,享元模式中一个类可以有多个享元对象,再由享元工厂在客户端使用时实现对于享元对象的共享。