享元模式可以避免大量且相似的开销。
例如,停车场中有1000辆车,那么需要创建1000个对象。这样开销是非常大的。
设这1000辆车中,Tesla有300辆,BMW有700辆。
以Tesla为例,300辆Tesla的基本属性都相同,仅仅是颜色不同,那么就考虑创建一个Tesla基础对象,然后300辆Tesla共享这一个对象,仅根据颜色不同进行各自的处理。
如,1号停车位的Tesla是红色,那么将Red这个外部状态传入给Tesla的共享对象,该对象将车的颜色更换为红色,然后绘制在停车位上;2号停车位的Tesla是蓝色,将Blue这个外部状态传入给Tesla的共享对象,该对象将车的颜色更换为蓝色。
BMW同理。
于是,1000辆车,只需要创建 2个对象。内部主体是共享的,仅需要根据传入的外部状态(非共享状态)进行一些处理。这样,会极大地节省开销。
这就是享元模式。
享元模式特征如下:
① 所有可共享的基础对象由一个享元工厂进行维护,即享元工厂维护一个享元池。如果池有所需要的基础对象,那么直接返回该基础对象;否则创建后放入池中,并返回
② 基础对象又称之为内部状态,是进行共享的部分;每个实际对象还有自己的特征,比如有的Tesla是红色,有的Tesla是蓝色,这些状态是不被共享的,是每个对象独有,称之为外部状态。外部状态由实际对象进行维护
1. 定义享元类及实际类
享元类即需要共享的类。该类定义了标准接口。同时该类具有一个内部状态变量。享元类对象可能有多个,不同的享元对象靠内部状态进行区分。
实际类是由享元类派生,该类是享元工厂所创建的实际类,含有对外部状态的处理函数。
class FlyWeight
{
public:
virtual ~FlyWeight() {};
virtual void Operation(const string& exState) {};
string GetInsState() { return this->_insState; }
protected:
//将构造函数设为protected,只有派生类可以访问
FlyWeight(string insState) { this->_insState = insState; }
private:
string _insState;
};
class ConFlyweight :public FlyWeight
{
public:
//将参数传给基类的构造函数,令基类可以初始化
ConFlyweight(string insState) :FlyWeight(insState) {};
//接受外部状态,并处理
void Operation(const string& exState) { this->_exState = exState; }
private:
string _exState;
};
2. 定义享元工厂
享元工厂维护一个享元池。该享元池是一个vector。
当需要的指定内部状态的实际享元对象存在于享元池中时,直接将该对象取出即可;否则创建后加入享元池中,并取出。
class FlyWeightFactory
{
public:
FlyWeight* GetFlyweight(const string& key)
{
vector<FlyWeight*>::iterator it = _fly.begin();
for (; it != _fly.end(); it++)
{
if ((*it)->GetInsState() == key)
{
//已创建
return *it;
}
}
FlyWeight* fn = new ConFlyweight(key);
_fly.push_back(fn);
return fn;
}
private:
vector<FlyWeight*> _fly;
};
3. 用户使用
void main()
{
FlyWeightFactory *fw = new FlyWeightFactory();
FlyWeight* fw1 = fw->GetFlyweight("Tesla");
FlyWeight* fw2 = fw->GetFlyweight("BMW");
FlyWeight* fw3 = fw->GetFlyweight("Tesla");
//根据外部状态进行处理
fw1->Operation("Red");
fw2->Operation("Green");
fw3->Operation("Blue");
}