结构型模式--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对象.