【设计模式】——享元模式
享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。
#include <iostream> #include <vector> using namespace std; //Flyweight类,它是所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态 class Flyweight { public: virtual void Operator(int extrinsicstate)=0; }; //ConcreteFlyweight是继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间 class ConcreteFlyweight:public Flyweight { public: void Operator(int extrinsicstate) { cout << "具体Flyweight:" << extrinsicstate << endl; } }; //UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但他并不强制共享 class UnsharedConcreteFlyweight:public Flyweight { public: void Operator(int extrinsicstate) { cout << "不共享的具体Flyweight:" << extrinsicstate << endl; } }; //FlyweightFactory,是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理的共享Flyweight, //当用于请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话) class FlyweightFactory { private: vector<Flyweight*> m_flyweights; public: FlyweightFactory() { Flyweight *temp=new ConcreteFlyweight(); m_flyweights.push_back(temp); } Flyweight *GetFlyweight(int key) { return m_flyweights.at(key); } }; int main() { int exetrinsicstate=22; FlyweightFactory *f=new FlyweightFactory(); Flyweight *fx=f->GetFlyweight(0); fx->Operator(exetrinsicstate); return 0; }
在享元对象内部并且不会随环境改变而改变的共享部分,可以成为是享元对象的内部状态,而随环境改变而改变的、不可以共享的状态就是外部状态了。事实上,享元模式可以避免大量非常相似的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。也就是说,享元模式Flyweight执行时所需的状态是有内部的也可能有外部的,内部状态存储于ConcreteFlyweight对象之中,而外部对象则应该考虑由客户端对象存储或计算,当调用Flyweight对象的操作时,将该状态传递给它。
下面是关于网站分类的例子:
#include <iostream> #include <map> using namespace std; //用户类,用于网站的客户帐号,是“网站”类的外部状态 class User { private: string m_name; public: User(string name) { this->m_name=name; } string GetName() { return m_name; } }; //网站抽象类 class WebSite { public: virtual void Use(User *user){} }; //具体网站类 class ConcreteWebSite:public WebSite { private: string m_name; public: ConcreteWebSite(string name) { this->m_name=name; } void Use(User *user) { cout << "网站分类:" << m_name << " 用户:" << user->GetName()<< endl; } }; //网站工厂类 class WebSiteFactory { private: map<string,WebSite*> m_flyweights; public: //获得网站分类 WebSite *GetWebSiteCategory(string key) { if(m_flyweights.find(key)==m_flyweights.end()) m_flyweights[key]=new ConcreteWebSite(key); return m_flyweights[key]; } }; int main() { WebSiteFactory *f=new WebSiteFactory(); WebSite *fx=f->GetWebSiteCategory("产品展示"); fx->Use(new User("小菜")); WebSite *fy=f->GetWebSiteCategory("产品展示"); fy->Use(new User("大鸟")); WebSite *f1=f->GetWebSiteCategory("博客"); f1->Use(new User("老顽童")); WebSite *fm=f->GetWebSiteCategory("博客"); fm->Use(new User("Awy")); return 0; }
如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时,就应该考虑使用;还有就是对象的大多数状态可以是外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。因为用了享元模式,所以有了共享对象,实例总数就大大减少了,如果共享的对象越多,存储节约也就越多,节约量随着共享状态的增多而增大。
在某些情况下,对象的数量可能会太多,从而导致了运行时的资源与性能损耗。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序,是一个值得去思考的问题,享元模式,可以运用共享技术有效地支持大量细粒度的对象。不过,你也别高兴得太早,使用享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要耗费资源,另外享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,使得程序的逻辑发扎花。因此,应当在有足够多的对象实例可供共享时才值得使用享元模式。