Flyweight

蝇量

运用共享技术有效地支持大量细粒度的对象。让某个类的一个实例用来提供许多“虚拟实例”。

适用性

  • 一个应用程序使用了大量的对象。
  • 完全由于使用大量的对象,造成很大的存储开销。
  • 对象的大多数状态都可变为外部状态。
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
  • 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

典型情境

一个文档中有很多字符,每个字符都是一个类。使用flyweight后,物理上每个字符共享一个flyweight对象,而这个对象出现在文档结构中的不同地方。一个特定字符对象的每次出现都指向同一个实例,这个实例位于flyweight对象的共享池中。

Flyweight Scenario
Flyweight Scenario Solution

一个场景里有很多树。使用flyweight后,应该只用一个树实例和一个客户对象来维护“所有”树的状态。

Flyweight Scenario 2

结构

Flyweight Class
Flyweight Object

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 设计模式