享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象[DP]。
- #include <vector>
- #include <iostream>
- using namespace std;
- class Flyweight
- {
- public:
- virtual void Operation(int extrinsicstate)=0;
- };
- class ConcreteFlyweight : public Flyweight
- {
- public:
- virtual void Operation(int extrinsicstate)
- {
- cout<<"ConcreteFlyweight"<<endl;
- }
- };
- //用来收容那些不需要共享的对象
- class UnsharedConcreteFlyweight : public Flyweight
- {
- public:
- virtual void Operation(int extrinsicstate)
- {
- cout<<"UnsharedConcreteFlyweight"<<endl;
- }
- };
- //一个享元工厂,用来创建并管理Flyweight对象。主要用来确保合理地共享Flyweight。
- //当用户请求一个Flyweight时,Flyweight对象提供一个已创建的实例或者创建一个(如果不存在的话)。
- class FlyweightFactory
- //本类和Flyweight是聚合关系,空心菱形加实线实心箭头,指向Flyweight类
- //Flyweight类对象作为其私有成员
- {
- private:
- vector<Flyweight*> vFlyweight;
- public:
- FlyweightFactory()
- {
- //按照某种方式构造Flyweight对象
- vFlyweight.push_back(new ConcreteFlyweight());
- vFlyweight.push_back(new ConcreteFlyweight());
- vFlyweight.push_back(new ConcreteFlyweight());
- }
- Flyweight* GetFlyweight(/*参数*/)
- {
- //按照参数找到某个对象,并返回
- return vFlyweight[0];
- }
- };
- //客户端代码
- //Client和FlyweightFactory是关联关系,实心箭头指向FlyweightFactory
- //Client和ConcreteFlyweight、UnsharedConcreteFlyweight都是关联关系
- void main()
- {
- //代码外部状态
- int extrinsicstate = 32;
- FlyweightFactory* f = new FlyweightFactory();
- //获取一个对象
- Flyweight* fx = f->GetFlyweight();
- fx->Operation(--extrinsicstate);
- fx = f->GetFlyweight();
- fx->Operation(--extrinsicstate);
- UnsharedConcreteFlyweight* uf = new UnsharedConcreteFlyweight();
- uf->Operation(--extrinsicstate);
- };
下面看一个具体的应用实例:如果有6个网站,分别用于blog、产品展示等等,我们没必要创建6个网站对象(如果不用享元的话,就需要6个Website类对象):看看代码是如何通过享元模式实现资源的节约的吧!
由于书上给出的例子是C#代码,我用C++改写之,没有使用Hashtable而用vector代替,所以不能编译运行下面的代码,表达一下意思即可。
- #include <string>
- #include <iostream>
- using namespace std;
- class Flyweight
- {
- };
- class Website
- {
- public:
- virtual void use() = 0;
- };
- class ConcreteWebsite : public Website
- {
- private:
- string name;
- public:
- ConcreteWebsite(string name)
- {
- this->name = name;
- }
- void use()
- {
- cout<<"网站分类:"<<name<<endl;
- }
- };
- class WebsiteFactory
- {
- private:
- vector<Flyweight*> vFlyweight;
- public:
- //按照key查找website
- Website* GetWebsiteCategory(string key)
- {
- //判断这个类型的网站对象是否存在,如果存在则直接返回
- return vFlyweight[key];
- //否则创建一个新的实例对象
- }
- int getWebsiteCount()
- {
- return vFlyweight.size();
- }
- };
- //客户端代码
- void main()
- {
- WebsiteFactory * f = new WebsiteFactory();
- //虽然使用了3次产品展示网站
- Website* fx = f->GetWebsiteCategory("产品展示");
- fx->use();
- fx = f->GetWebsiteCategory("产品展示");
- fx->use();
- fx = f->GetWebsiteCategory("产品展示");
- fx->use();
- //使用了两次blog网站
- Website* f1 = f->GetWebsiteCategory("blog");
- f1->use();
- f1 = f->GetWebsiteCategory("blog");
- f1->use();
- f1 = f->GetWebsiteCategory("blog");
- f1->use();
- //统计实例的个数,仍然是2个
- cout<<f->getWebsiteCount();
- };
也就是说,不管建几个网站,只要是“产品展示”,它们的代码都是一样的;blog网站也类似。(可想而知,它们公用一个数据库,当不同的用户以不同的身份登录时,返回数据库中的不同数据即可)
享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数[1]移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
对于Flyweight执行时的状态,有内部的也有外部的:内部的存储于ConcreteFlyweight中,外部对象则由客户端对象存储或计算,在调用Flyweight对象的操作时,再将状态传递给ConcreteFlyweight。
为了体现“不同对象返回不同数据”,看下面重载的客户端代码:
- void main()
- {
- WebsiteFactory* f =new WebsiteFactory();
- Website* fx = f->GetWebsiteCategory("产品展示");
- fx->use(new User("小菜"));
- //类似上面的代码,可以创建不同的用户,作为use函数的参数
- };
[1] 在示例中,就是指的new User("sombody")这个参数啦!