1.动机
- 在软件系统中采用存粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价:主要是内存需求方面的代价
- 如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式进行操作?
正常情况下可能看不出来,但是如果类的数量达到了几万个,甚至于几十万个,上面问题就会暴露出来。
80年代中后期有一种思潮:一切皆对象,后面实现下来发现有点不太现实,比如一个int,这种类型没有必要用对象,但是在业务场景中,也是有一些很小的东西,看起来很细粒度,但是在设计上可能也会考虑将其设计成对象。而且如果用的量特别大,就会引来内存的开销。怎么办呢?
其实思路就是,能不能使用共享的方式(享元名字的由来)
2. 模式定义
运用共享技术有效地支持大量细粒度的对象
---《设计模式》GOF
3. 结构
本来细粒度的对象,可能都是直接new出来的,但是这里使用了一个享元工厂,通过一个key判断对象是否存在,存在的话就不需要重新new了。
右下角的两个(可以忽略这部分)表示有的是支持享元的,有的是不支持享元的
4. 代码示例
比如设计一个字符处理系统,假设把字体设计成一个对象,字体对象在系统中需求量很大,严格意义上讲,每一个字符都有一个字体。不过你看大量的文章会发现,他们使用的字体也就几种而已,绝大多说字符的字体都是想同的。如果不加区分的为每一个字符创建一个对象的话,很显眼是不合理的。
class Font{
private:
string key;
//
// ...
public:
Font(const string& key){
//...
}
};
class FontFactory{
private:
map<string, Font*> fontPool;
public:
Font* GetFont(const string &key){
auto iter = fontPool.find(key);
if (iter != fontPool.end()){
return fontPool[key];
}
else{
Font *font = new Font(key);
fontPool[key] = font;
return font;
}
}
};
5. 要点总结
- 面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight主要解决面向对象中的代价问题,一般不触及面向对象的抽象性问题。
- Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象对系统带来的内存压力。在具体实现方面,要注意对对象状态的处理(因为我们创建完对象之后,对象的状态是一般情况下是不能更改的,往往这种对象创建出来是只读的)