蝇量
运用共享技术有效地支持大量细粒度的对象。让某个类的一个实例用来提供许多“虚拟实例”。
适用性
- 一个应用程序使用了大量的对象。
- 完全由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
典型情境
一个文档中有很多字符,每个字符都是一个类。使用flyweight后,物理上每个字符共享一个flyweight对象,而这个对象出现在文档结构中的不同地方。一个特定字符对象的每次出现都指向同一个实例,这个实例位于flyweight对象的共享池中。
一个场景里有很多树。使用flyweight后,应该只用一个树实例和一个客户对象来维护“所有”树的状态。
结构
Flyweight - 描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。
Concrete Flyweight - 实现Flyweight接口,并为内部状态(如果有的话)增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的;即,它必须独立于ConcreteFlyweight对象的场景。
UnsharedConcreteFlyweight - 并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
FlyweightFactory - 创建并管理flyweight对象。确保合理地共享flyweight。当用户请求一个flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。
Client - 维持一个对flyweight的引用。计算或存储一个(多个)flyweight的外部状态。
协作:
flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。
用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #include <iostream> #include <string> const int MAX_NUM = 255; //--------------------------------------------------- class ExtrinsicState { public: void set_num(int n) {m_num = n;} int get_num() const {return m_num;} private: int m_num; }; //--------------------------------------------------- class Flyweight { public: virtual void operation(ExtrinsicState& context){} }; //--------------------------------------------------- class ConcreteFlyweight : public Flyweight { public: ConcreteFlyweight(char c) : Flyweight(), m_inner(c) { std::cout << "Shared Created once."<<std::endl; } virtual void operation(ExtrinsicState& context) { std::string temp(context.get_num(), m_inner); std::cout<<temp<<std::endl; } private: char m_inner; }; //--------------------------------------------------- class UnsharedConcreteFlyweight : public Flyweight { public: UnsharedConcreteFlyweight() : Flyweight() { std::cout << "Unshared Created once."<<std::endl; } virtual void operation(ExtrinsicState& context) { std::cout<<"I don't like share."<<std::endl; } }; //--------------------------------------------------- class FlyweightFactory { public: FlyweightFactory() { for (int i = 0; i < MAX_NUM; ++i) { m_flyweights[i] = NULL; } } virtual Flyweight* create_concrete_flyweight(char c) { if (!m_flyweights[c]) {m_flyweights[c] = new ConcreteFlyweight(c);} return m_flyweights[c]; // 此处简单以ConcreteFlyweight的intrinsicState,即char m_inner,作为索引。 } virtual Flyweight* create_unshared_concrete_flyweight() { return new UnsharedConcreteFlyweight(); } private: Flyweight* m_flyweights[MAX_NUM]; }; //--------------------------------------------------- class Client { public: void test() { ExtrinsicState ex; ex.set_num(3); FlyweightFactory factory; Flyweight* share1 = factory.create_concrete_flyweight('c'); Flyweight* share2 = factory.create_concrete_flyweight('c'); share1->operation(ex); share2->operation(ex); Flyweight* unshare1 = factory.create_unshared_concrete_flyweight(); Flyweight* unshare2 = factory.create_unshared_concrete_flyweight(); unshare1->operation(ex); unshare2->operation(ex); } }; //--------------------------------------------------- int main() { Client s; s.test(); system("Pause"); }
|
优点
- 减少运行时对象实例的个数,节省内存。
- 将许多“虚拟”对象的状态集中管理。
用途和缺点
- 当一个类有许多的实例,而这些实例能被同一方法控制的时候,我们就可以使用蝇量模式。
- 蝇量模式的缺点在于,一旦你实现了它,那么单个的逻辑实例将无法拥有独立而不同的行为。
相关模式
- Flyweight模式通常和Composite模式结合起来,用共享叶节点的有向无环图实现一个逻辑上的层次结构。
- 最好用Flyweight实现State和Strategy对象。
[1] 设计模式:可复用面向对象软件的基础
[2] Head First 设计模式