结构型模式--Flyweight模式(享元)
运用共享技术有效地支持大量细粒度的对象.
Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它.
当以下情况都成立时使用Flyweight模式:
1. 一个应用程序使用了大量的对象。
2. 完全由于使用大量的对象,造成很大的存储开销。
3. 对象的大多数状态都可变为外部状态。
4. 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
5. 应用程序不依赖于对象标识.
图1
Flyweight
—描述一个接口, 通过这个接口flyweight可以接受并作用于外部状态.
ConcreteFlyweight
—实现Flyweight接口, 并为内部状态, 提供存储空间. ConcreteFlyweight对象必须是可共享的. 它所存储的状态必须是内部的:即它必须独立于ConcreteFlyweight对象的场景(独立于上下文), 客户在调用其操作时, 需要把外部状态传递给它使用(但不会保存).
UnsharedConcreteFlyweight
—并非所有的Flyweight子类都需要被共享. Flyweight接口使共享成为可能, 但它并不强制共享. 在Flyweight对象结构的某些层次, UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点.
FlyweightFactory
—创建并管理Flyweight对象。
—确保合理地共享Flyweight. 当用户请求一个Flyweight时, FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)(创建ConcreteFlyweight)
Client
—维持一个对Flyweight的引用。
—计算或存储一个(多个)Flyweight的外部状态。
Flyweight执行时所需的状态必定是内部的或外部的. 内部状态存储于ConcreteFlyweight对象之中, 而外部对象则由Client对象存储或计算. 当用户调用Flyweight对象的操作时, 将该状态传递给它. 用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象, 这可以保证对它们适当地进行共享.
// 创建ConcreteFlyweight对象(存储内部状态的对象) FlyweightFactory::GetFlyweight(key) { if(flyweight[key] exists) // 这里的创建策略可以根据需求而定. return existing flyweight // 你可以一开始就直接创建. else // 但如果为了省空间, 那就需要时才创建也可以. new ConcreteFlyweight add it to pool of flyweights return it } // 客户 // 获得key对应的享元对象. ConcreteFlyweight* pConcreteFlyweight = FlyweightFactory::GetFlyweight(key); // 计算外部状态 extrinsicState = ..... // 外部状态由客户管理, 计算也好, 存储也好. // 调用享元对象的某些操作 pConcreteFlyweight->Operation(extrinsicState); // pConcreteFlyweight对象的内部状态自己存储, 所以不需要传递. // 这里没有用到UnsharedConcreteFlyweight(暂时不是很明白),
Flyweight模式经常和Composite模式结合起来表示一个层次式结构, 这一层次式结构是一个共享叶节点的图. 共享的结果是, Flyweight的叶节点不能存储指向父节点的指针. 而父节点的指针将传给Flyweight作为它的外部状态的一部分. 这对于该层次结构中对象之间相互通讯的方式将产生很大的影响(不是很明白!).
共享的Flyweight越多, 存储节约也就越多. 节约量随着共享状态的增多而增大. 当对象使用大量的内部及外部状态, 并且外部状态是计算出来的而非存储的时候, 节约量将达到最大. 所以, 可以用两种方法来节约存储: 用共享减少内部状态的消耗, 用计算时间换取对外部状态的存储.
1. 你必须正确分离出内部状态与外部状态.
2. 外部状态由客户管理.
3. 内部状态需要存储下来共享.
4. 如果外部状态不是通过计算出来的, 内存就省不下来了.(用时间换空间).
5. 假设有10个内部状态, 1000个外部状态, 如果他们不分离开, 那就代表需要 1000 个单位的存储空间, 因为即使那1000个外部状态可以通过计算得到, 但是由于内外部没有分离没共享, 那么还是需要1000个对象来存储内部状态.
6. Flyweight模式把内部状态和外部状态分离, 假设1000个外部状态中, 有100个是不能动态计算出来的(它们又不能作为内部状态), 100 个单位空间是省不了的.
7. FlyweightFactory需要管理好Flyweight对象.
Flyweight模式通常和Composite模式结合起来, 用共享叶结点的有向无环图实现一个逻辑上的层次结构. 通常, 最好用Flyweight实现State和Strategy对象.