【转】设计模式学习笔记(11)-享元
享元(Flyweight)模式,顾名思义即利用共享以支持大量细粒度对象的技术。这里对Flyweight的翻译还是比较文艺的,Flyweight意为轻量的,这个模式就是合理地利用共享对象,使用轻量级的解决方案来解决存储大量的对象的问题。
我们经常会遇到某些拥有大量对象的设计,如文本编辑器,可用的字符不多,汉字也就几千个,但一篇文章往往会超过这个数量级,如果没每个文字都建立一个全新的对象,则会对系统造成巨大的开销,相反如果我们为文章中重复的字符只建立一个副本,所有该文字出现的位置改为对其的引用,这样会大量减少对内存的占用。用类图表示为:
这里,FlyweightFactory是产生Flyweight的一个工厂,client可以通过getFlyweight方法取得,它有一个参数key,因此其中中必然存在类似于Map之类的数据结构存储Flyweight对象。
Flyweight类是一个抽象类,它有两个子类ConcreteFlyweight和UnsharedConcreteFlyweight,分别对应共享的享元对象和不共享的享元对象。因为不是所有的Flyweight子类都必须是共享的,UnsharedConcreteFlyweight类使得共享变得更灵活。
我们下面的代码中将只考虑共享的部分:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Flyweight{public: virtual void set(string sth) = 0; virtual void operation() = 0;};class ConcreteFlyweight : public Flyweight{private: string something;public: void set(string sth) { something = sth; } void operation() { cout << something << endl; }}; |
这里可以不用Flyweight虚类,因为只有一个子实现,类中有两个方法。set方法用于设置对象的个性信息,Operation则是一些通用操作。关键是实现工厂类:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class FlyweightFactory {private: map<string, Flyweight*> list;public: Flyweight* getFlyweight(string key) { map<string, Flyweight*>::iterator itr; itr = list.find(key); if(itr != list.end()) { return itr->second; } else { Flyweight* fly = new ConcreteFlyweight; list.insert(pair<string, Flyweight*>(key, fly)); return fly; } }}; |
这里使用了map关联结构,其专业术语叫共享池,当客户请求获取对象时,如果池中有这个对象则直接返回,否则new一个对象返回并将该对象放入共享池中。
为了简单化我们并没有使用单例模式,实际编码中最好使用该模式。
在main方法里这样写:
|
1
2
3
4
5
|
FlyweightFactory* factory = new FlyweightFactory;Flyweight* fly = factory->getFlyweight("123");fly->set("123");Flyweight* fly2 = factory->getFlyweight("123");fly2->operation(); |
为了说明问题我们从factory中取了两次123所对应的对象,并在第一次取出后做了set标记,在第二次取出后我们打印出这个标记:
|
1
|
123 |
结果很显然是刚才设置的,这说明两次取得的对象其实是一个,这便达到了共享的目的。
上面表述的享元的基本结构,然而真正的运用确实不简单的。由于多个相同对象往往并不是完全相同的,这怎么理解呢?我们在文本编辑器中,仅仅有文字是不够的,当然纯文本除外。我们还需要格式控制信息,如字体、颜色等等,相同的字在不同的位置可能并不是完全相同的。怎么办?我们可以将格式信息剥离出来,作为一个外部对象存贮,将外部对象与享元对象结合起来便构成了我们想要的样子。
我们也应该看到享元的不足之处,享元是以时间间换取空间,由于获得对象多了一步,使得计算量加大,因此使用这种模式之前需要进行权衡。
最后来看看享元模式的适用性:
- 一个应用程序使用了大量的对象
- 完全由于使用大量对象造成了很大的存储开销
- 对象的大多数状态都可变为外部状态
- 如果删除对象的外部状态,那么可以用相对较小的共享对象取代很多的对象
- 应用程序不依赖于对象标识
享元模式和组合模式经常结合起来使用,同样,在后来要学习的状态模式和策略模式中也十分常见。
posted on 2013-03-05 15:46 TheKingOfKingFish 阅读(130) 评论(0) 收藏 举报

浙公网安备 33010602011771号