1、定义与动机
-
概述:享元模式和单例模式一样,都是为了解决程序的性能问题。面向对象很好地解决了"抽象"的问题,但是必不可免得要付出一定的代价。对于通常情况来讲,面向对象的成本大豆可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。
-
定义:运用共享技术有效地支持大量细粒度的对象。
-
动机:
- 在软件系统中采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,而带来很高的运行时代价——主要指内存需求方面的代价。
- 如何避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?
-
个人理解:例如在电脑中存在大量的字体
-
首先:电脑在启动时不可能把所有的字体的文件渲染方式或者字体样式都加载到内存中,而应该是在需要的时候去加载
-
其次:当电脑字体需要更改时会创建一个字体对象,而wps/office中的字体也会有一个对象字体。
-
当word和ppt中的字体相同时,应该是只需要创建一个对象,因为字体这个东西应该说只读(只用来渲染,总不可能更改样式吧),因此这两个对象应该使用同一个对象即可,不应该在需要的地方就new!
-
很明显这里也是解决一个性能问题的设计模式
-
比较常见的一个实用地方:各种池子,线程池、内存池、连接池…,它们都应该创建好需要的时候去取一个就行,用完了还回去;而不是需要的时候new一个。
-
2、享元模式
- 对于Font类创建出来的对象应该只能读,不允许修改
- FontFactory字体工厂负责取字体,如果没有就new一个字体然后存放在一个数据结构中(这里用无序map)
#include <iostream>
#include <unordered_map>
#include <string>
class Font{
private:
// unique object key
std::string key;
// object state ...
public:
Font(const std::string &_key){
// ...
}
};
class FontFactory{
private:
std::unordered_map<std::string, Font*> fontPool;
public:
Font *GetFont(const std::string &key){
if(fontPool.find(key) == fontPool.end()){
Font *font = new Font(key);
fontPool.insert(std::make_pair(key, font));
}
return fontPool[key];
}
};
void process()
{
FontFactory* fontFactory = new FontFactory();
Font* songStyle = fontFactory->GetFont("宋体");
Font* kaiStyle = fontFactory->GetFont("楷体");
// ...
}
其实这里没有设计好:
- FontFactory可以到处创建,可以考虑把FontFactory字体工厂做成一个单例的工厂。
- 字体获取的多线程安全问题,准确来说获取应该是没有线程安全问题的,创建有…
3、总结
- 面向对象很好的解决了抽象性问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight主要解决面向对象的代价问题,一般不初级面向队形的抽象性问题
- Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象的状态处理(即只读不修改,否则后续可能无法重复使用)
- 对象的数量太大从而导致对象内存开销加大——什么样的数量才算大?这需要仔细的根据具体应用情况进行评估,而不能凭空臆断。