简介
动机
- 假设成立一个外包公司,主要给一些私营业主建网站。
- 商家客户都类似,要求也就是信息发布、产品展示、博客留言、论坛等功能。
- 各个客户要求差别不大,但客户数量多。
内部状态和外部状态
- 在享元对象内部并且不会随环境改变而改变的共享部分,可以成为是享元对象的内部状态。
- 随环境改变而改变的、不可以共享的状态就是外部状态。
- 享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数外基本都是相同的,有时就能大幅度减少需要实例化的类的数量。
- 如果能把那些参数移到类实例外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
适用性
- 当以下情况成立时使用享元模式:
- 一个应用程序使用大量的对象,并且完全是由于使用大量的对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态,并且如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象表示。由于Flyweight对象可以被共享,对于概念上明显有别的对象,表示测试将返回真值。
结构
参与者
- Flyweight
- 描述一个接口,通过这个接口Flyweight可以接受并作用于外部状态。
- ConcreteFlyweight
- 实现Flyweight接口,并为内部状态增加空间。ConcreteFlyweight对象必须是可共享的。它所存储状态必须是内部的;即,它必须独立于ConcreteFlyweight的场景。
- UnsharedConcreteFlyweight
- 并非所有的Flyweight子类都需要被共享。 Flyweight接口使共享成为可能,但它并不强制共享。
- FlyweightFactory
- 创建并管理flyweight对象。
- 确保合理地共享flyweight。当用户请求一个flyweight时,Flyweight对象提供一个已创建的实例或者创建一个(如果不存在的话)。
- Client
- 维持一个对flyweight的引用。
- 计算或存储一个(多个)flyweight的外部状态。
协作
- Flyweight执行时所需状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。
- 用户不直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。
实例
假设成立一个外包公司,主要给一些私营业主建网站。商家客户要求的功能包括信息发布、产品展示、博客留言等功能。
共有a-j 10个客户,其中a-c客户需要信息发布,d-f需要产品展示,g-j客户需要博客功能。
利用享元模式模拟该公司建立网站,输出如下:
客户a: 信息发布
客户b:信息发布
。。。
网站分类总数为:3
UML
代码
在本次实验中:客户的号码就是外部状态,应该由专门的对象来处理,在这里简化为char类型的id。
另外注意没有UnsharedConcreteFlyweight类
Website类即Flyweight类
ConcreteWebsite类即ConcreteFlyweight类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#include<iostream> #include<map> #include<vector> using namespace std; /* 假设成立一个外包公司,主要给一些私营业主建网站。商家客户要求的功能包括信息发布、产品展示、博客留言等功能。 共有a-j 10个客户,其中a-c客户需要信息发布,d-f需要产品展示,g-j客户需要博客功能。 利用享元模式模拟该公司建立网站,输出如下: 客户a: 信息发布 客户b:信息发布 。。。 网站分类总数为:3 */ class Website{ public: virtual void Operation(char extrinsicstate) = 0; }; class ConcreteWebsite: public Website { public: ConcreteWebsite(string function){ this->function = function; } void Operation(char extrinsicstate){ cout<<"客户"<<extrinsicstate<<": "<<function<<endl; } private: string function; }; class FlyweightFactory{ private: map<string, ConcreteWebsite*> flyweights; public: FlyweightFactory(){ flyweights.insert(make_pair("信息发布", new ConcreteWebsite("信息发布"))); flyweights.insert(make_pair("产品展示", new ConcreteWebsite("产品展示"))); flyweights.insert(make_pair("博客功能", new ConcreteWebsite("博客功能"))); } Website* GetFlyweight(string key){ if(! flyweights.count(key)){ flyweights.insert(make_pair(key, new ConcreteWebsite(key))); cout<<"添加状态: "<<key<<"成功"<<endl; } return (Website*) flyweights[key]; } int getSize(){ return flyweights.size(); } }; int main(){ FlyweightFactory *factory = new FlyweightFactory(); vector<Website*> pool; for(char i = 'a'; i <= 'j'; i++){ Website *f; if(i >= 'a' && i <= 'c'){ f = factory->GetFlyweight("信息发布"); f->Operation(i); }else if(i >= 'd' && i <= 'f'){ f = factory->GetFlyweight("产品展示"); f->Operation(i); }else{ f = factory->GetFlyweight("博客功能"); f->Operation(i); } pool.push_back(f); } cout<<"网站分类总数为:"<<factory->getSize()<<endl; //if(pool[0] == pool[1])cout<<"THEY ARE THE SAME!"<<endl; return 0; } |
结果
总结
①本次实验掌握了享元模式的原理与方法,在选择flyweight时,要注意选择合适的享元类。
②按照实际情况分情况讨论具体的Flyweight类。
③不管建几个网站,只要是‘产品展示’,都是一样的,只要是‘博客’,也是完全相同的,但这样是有问题的,给企业建的网站不是一家企业,它们的数据不会相同,所以至少它们都应该有不同的账号(id值)。实际上这样写没有体现对象间的不同,只体现了它们共享的部分。