1、定义
运用共享技术,有效的支持大量细粒度的对象。
程序中存在大量细粒度的对象,每次要使用时都必须创建一个新的对象,既影响了运行效率又增加了内存消耗。于是有了享元模式,享元模式提取出这些细粒度对象中间公共的状态(属性,我的理解),只生成一个实例对象,所有用到这些公共属性对象的地方,都指向这一个实例。
很多类有一部分的属性是可以共享的,而不可共享的部分需要提取出来,通过参数传递来使用。
内部状态:在享元对象内部并且不会随环境改变而改变的共享部分。
外部状态:随环境改变而改变的、不可以共享的状态是享元对象的外部状态。
外部状态的变化不会引起内部状态的变化。由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。也就是说,享元模式的本质是分离与共享 : 分离变与不变,并且共享不变。把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的;然后通过共享不变的部分,达到减少对象数量并节约内存的目的。
享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外部状态与内部状态是相互独立的。
由于享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态设置为相同部分。在我们的程序设计过程中,我们可能会需要大量的细粒度对象来表示对象,如果这些对象除了几个参数不同外其他部分都相同,这个时候我们就可以利用享元模式来大大减少应用程序当中的对象。如何利用享元模式呢?这里我们只需要将他们少部分的不同的部分当做参数移动到类实例的外部去,然后在方法调用的时候将他们传递过来就可以了。这里也就说明了一点:内部状态存储于享元对象内部,而外部状态则应该由客户端来考虑。
例如:休闲开发游戏中的围棋、五子棋等,它们都有大量的棋子对象,围棋和五子棋只有黑白两种颜色,所以颜色是棋子的内部状态;而各个棋子之间的差别是主要就是位置的不同,所以方位坐标是棋子的外部状态。若使用享元模式处理棋子,那么棋子对象可以减少到只有两个实例。
什么时候使用?
若一个应用程序使用了大量对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
对象的大多数状态可以外部状态,若删除对象的外部状态,则可以用相对较少的共享对象取代很多组对象,此时可考虑使用享元模式。
用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在有足够多的对象实例可供共享时才值得使用享元模式。
优点:
(1)它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份;
(2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
缺点:
(1)享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化;
(2)为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
实现:
享元模式运用共享技术有效地支持大量 细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用,它是一种对象结构型模式。
享元模式包含四个角色:抽象享元类声明一个接口,通过它可以接受并作用于外部状态;具体享元类实现了抽象享元接口,其实例称为享元对象;非共享具体享元是不能被共享的抽象享元类的子类;享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中。
享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态和外部状态。其中内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享;外部状态是随环境改变而改变的、不可以共享的状态。
Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象;
FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class User//外部状态类
{
private:
string name;//外部状态
public:
User(string name1):name(name1){}
string getName(){return name;}
};
//基类,定义操作接口Operation,
class Flyweight
{
public:
//操作外部状态extrinsicState
virtual void Operation(User *user)=0;
virtual ~Flyweight(){}
Flyweight(){}
virtual string GetIntrinsicState(){}
};
class ConcreteFlyweight:public Flyweight
{
private:
//内部状态
string _intrinsicState;
public:
ConcreteFlyweight(string intrinsicState):_intrinsicState(intrinsicState){}
//实现接口函数
virtual void Operation(User *user)
{
cout << this->GetIntrinsicState() << " " << user->getName() << endl;
}
~ConcreteFlyweight(){}
string GetIntrinsicState()
{
return this->_intrinsicState;
}
};
class UnsharedConcreteFlyweight:public Flyweight
{
public:
virtual void Operation(User *user)
{
cout << user->getName() << endl;
}
UnsharedConcreteFlyweight(string intrinsicState){}
~UnsharedConcreteFlyweight(){}
};
class FlyweightFactory
{
public:
FlyweightFactory(){}
~FlyweightFactory(){}
//获得一个请求的Flyweight对象
Flyweight* GetFlyweight(string key)//若该对象已存在,直接返回,否则新建一个对象,存入容器中,再返回
{
vector<Flyweight*>::iterator iter = this->m_vecFly.begin();
for(;iter!= this->m_vecFly.end();iter++)
{
if((*iter)->GetIntrinsicState() == key)
return *iter;
}
Flyweight* fly = new ConcreteFlyweight(key);
this->m_vecFly.push_back(fly);
return fly;
}
//获取容器中存储的对象数量
void GetFlyweightCount()
{
cout << this->m_vecFly.size() << endl;
}
protected:
private:
//保存内部状态对象的容器
vector<Flyweight*> m_vecFly;
};
int main()//客户端
{
//工厂对象,工厂对象
FlyweightFactory* fc = new FlyweightFactory();
//向工厂申请一个Flyweight对象,且该对象的内部状态值为“hello”
Flyweight* fly = fc->GetFlyweight("内部状态1");
//应用外部状态
fly->Operation(new User("外部状态1"));
Flyweight* fly1 = fc->GetFlyweight("内部状态1");
fly1->Operation(new User("外部状态2"));
Flyweight* fly2 = fc->GetFlyweight("内部状态2");
fly2->Operation(new User("外部状态3"));
fc->GetFlyweightCount();//2
return 0;
}