1、定义
1.1 标准定义
享元模式( Flyweight Pattern) 是池技术的重要实现方式, 其定义如下: Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。)
享元模式的定义为我们提出了两个要求: 细粒度的对象和共享对象。 我们知道分配太多的对象到应用程序中将有损程序的性能, 同时还容易造成内存溢出, 那怎么避免呢? 就是享元模式提到的共享技术。 我们先来了解一下对象的内部状态和外部状态。要求细粒度对象, 那么不可避免地使得对象数量多且性质相近, 那我们就将这些对象的信息分为两个部分: 内部状态( intrinsic) 与外部状态( extrinsic) 。
● 内部状态
内部状态是对象可共享出来的信息, 存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息, 不必直接储存在具体某个对象中, 属于可以共享的部分。
● 外部状态
外部状态是对象得以依赖的一个标记, 是随环境改变而改变的、 不可以共享的状态, 它是一批对象的统一标识, 是唯一的一个索引值。
● 外部状态和内部状态区别
在Flyweight模式应用中,通常修改的是外部状态属性,而内部状态属性一般都是用于参考或计算时引用。Flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部状态则由Client对象存储或计算。当用户调用Flyweight对象的操作时,将该状态传递给它。
1.2 通用类图
● Flyweight——抽象享元角色
它简单地说就是一个产品的抽象类, 同时定义出对象的外部状态和内部状态的接口或实现。
● ConcreteFlyweight——具体享元角色
具体的一个产品类, 实现抽象角色定义的业务。 该角色中需要注意的是内部状态处理应该与环境无关, 不应该出现一个操作改变了内部状态, 同时修改了外部状态, 这是绝对不允许的。
● unsharedConcreteFlyweight——不可共享的享元角色
不存在外部状态或者安全要求( 如线程安全) 不能够使用共享技术的对象, 该对象一般不会出现在享元工厂中。
● FlyweightFactory——享元工厂
职责非常简单, 就是构造一个池容器, 同时提供从池中获得对象的方法。
2、实现
以文字处理软件为例:
内部状态存储于flyweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享。如字符代码,字符大小等。
外部状态取决于flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态传递给flyweight,如字符位置,字符颜色等。
2.1 类图
Flyweight:享元类的基类,定义一个接口,通过这个接口Flyweight可以接受并作用于外部状态。
ConcreteFlyweight:实现Flyweight接口, 并为内部状态( 如果有的话) 增加存储空间。ConcreteFlyweight对象必须是可共享的。它所存储的状态必须是内部的(intrinsic);即,它必须独立于ConcreteFlyweight对象的场景。
UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。
FlyweightFactory:创建并管理Flyweight对象;确保合理地共享Flyweight。当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。
Client:维持一个对Flyweight的引用;计算或存储一个(多个)Flyweight的外部状态。
实现要点:
● 享元工厂维护一张享元实例表。
● 享元不可共享的状态需要在外部维护。即删除外部状态:该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共享对象中删除。
● 按照需求可以对享元角色进行抽象。
● 管理共享对象:引用计数和垃圾回收。
2.2 代码
2.2.1 享元代码
// Flyweight.h #ifndef _FLYWEIGHT_H_ #define _FLYWEIGHT_H_ #include <string> #include <vector> using namespace std; //基类,定义操作接口Operation class Flyweight { public: //操作外部状态extrinsicState virtual void Operation(const string& extrinsicState)=0; string GetIntrinsicState(); virtual ~Flyweight(); protected: Flyweight(string intrinsicState); private: //内部状态,也可以放在ConcreteFlyweight中 string _intrinsicState; }; class ConcreteFlyweight:public Flyweight { public: //实现接口函数 virtual void Operation(const string& extrinsicState); ConcreteFlyweight(string intrinsicState); ~ConcreteFlyweight(); }; class UnsharedConcreteFlyweight:public Flyweight { public: virtual void Operation(const string& extrinsicState); UnsharedConcreteFlyweight(string intrinsicState); ~UnsharedConcreteFlyweight(); }; class FlyweightFactory { public: FlyweightFactory(); ~FlyweightFactory(); //获得一个请求的Flyweight对象 Flyweight* GetFlyweight(string key); //获取容器中存储的对象数量 void GetFlyweightCount(); protected: private: //保存内部状态对象的容器 vector<Flyweight*> m_vecFly; }; #endif
// Flyweight.cpp #include "Flyweight.h" #include <iostream> using namespace std; Flyweight::Flyweight(string intrinsicState) { this->_intrinsicState = intrinsicState; } Flyweight::~Flyweight(){} string Flyweight::GetIntrinsicState() { return this->_intrinsicState; } ConcreteFlyweight::ConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState){} ConcreteFlyweight::~ConcreteFlyweight(){} void ConcreteFlyweight::Operation(const string& extrinsicState) { cout << this->GetIntrinsicState() << endl; cout << extrinsicState << endl; } UnsharedConcreteFlyweight::UnsharedConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState){} UnsharedConcreteFlyweight::~UnsharedConcreteFlyweight(){} void UnsharedConcreteFlyweight::Operation(const string& extrinsicState) { cout << "extrinsicState" << endl; } FlyweightFactory::FlyweightFactory(){} FlyweightFactory::~FlyweightFactory(){} //若该对象已存在,直接返回,否则新建一个对象,存入容器中,再返回 Flyweight* FlyweightFactory::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 FlyweightFactory::GetFlyweightCount() { cout << this->m_vecFly.size() << endl; }
2.2.2 调用
// main.cpp #include "Flyweight.h" #include <iostream> #include <string> using namespace std; int main() { //外部状态extrinsicState string extrinsicState = "ext"; //工厂对象,工厂对象 FlyweightFactory* fc = new FlyweightFactory(); //向工厂申请一个Flyweight对象,且该对象的内部状态值为“hello” Flyweight* fly = fc->GetFlyweight("hello"); Flyweight* fly1 = fc->GetFlyweight("hello"); //应用外部状态 fly->Operation(extrinsicState); fc->GetFlyweightCount(); return 0; }
2.3 另一种文本处理实现
2.3.1 文档类
// Document.h #ifndef _DOCUMENT_H_ #define _DOCUMENT_H_ #include <string> #include <vector> using namespace std; // The 'Flyweight' abstract class class character { public: //析构函数 virtual ~character(); //应用外部状态 virtual void Display(int width,int height,int ascent,int descent,int pointSize)=0; //获取内部状态 virtual char GetSymbol()=0; protected: /*-----内部状态---------*/ char symbol; /*----------------------/ /*-----外部状态---------*/ int width; int height; int ascent; int descent; int pointSize; /*----------------------*/ //构造函数 character(char c); }; //A 'ConcreteFlyweight' class class characterA:public character { public: characterA(char c); ~characterA(); virtual void Display(int width,int height,int ascent,int descent,int pointSize); virtual char GetSymbol(); protected: private: }; //B 'ConcreteFlyweight' class class characterB:public character { public: characterB(char c); ~characterB(); virtual void Display(int width,int height,int ascent,int descent,int pointSize); protected: private: }; //C 'ConcreteFlyweight' class class characterC:public character { public: characterC(char c); ~characterC(); virtual void Display(int width,int height,int ascent,int descent,int pointSize); protected: private: }; //D 'ConcreteFlyweight' class class characterD:public character { public: characterD(char c); ~characterD(); virtual void Display(int width,int height,int ascent,int descent,int pointSize); protected: private: }; /* ... */ //The 'FlyweightFactory' class class characterFactory { public: characterFactory(); ~characterFactory(); //申请一个character对象 character* GetCharacter(char); //获取存储的character*数量 vector<character*>::size_type GetCount(); private: //保存character*的容器,可以换成List等其它容器 vector<character*> m_vecCharacter; }; #endif
// Document.cpp #include "Document.h" #include <iostream> character::character(char c) { this->symbol = c; } character::~character(){} characterA::characterA(char c):character(c){} characterA::~characterA(){} char characterA::GetSymbol() { return this->symbol; } void characterA::Display(int width,int height,int ascent,int descent,int pointSize) { //接收并作用外部状态 this->ascent = ascent; this->descent = descent; this->height = height; this->pointSize = pointSize; this->width = width; cout << this->symbol <<" " << this->ascent <<" " << this->descent <<" " << this->height <<" " << this->pointSize <<" " << this->width << endl; } characterFactory::characterFactory(){} characterFactory::~characterFactory(){} character* characterFactory::GetCharacter(char c) { vector<character*>::iterator iter = this->m_vecCharacter.begin(); for(;iter != this->m_vecCharacter.end();iter++) { if((*iter)->GetSymbol() == c) { return *iter; } } character* pf = new characterA(c); this->m_vecCharacter.push_back(pf); return pf; } vector<character*>::size_type characterFactory::GetCount() { return this->m_vecCharacter.size(); }
2.3.2 调用
// main.cpp #include "Flyweight.h" #include "Document.h" #include <iostream> #include <string> using namespace std; int main() { //存储外部状态 int ascent = 70; int descent = 0; int height = 100; int width = 120; int pointSize = 10; string test = "AABCDDEFGHI"; string::iterator it = test.begin(); characterFactory* pcF = new characterFactory(); for(;it!=test.end();it++) { pointSize++; char c = *it; //申请一个character对象 character* charc= pcF->GetCharacter(c); //应用外部状态 charc->Display(width,height,ascent,descent,pointSize); } vector<character*>::size_type sizeChar = pcF->GetCount(); cout << "count:" << sizeChar << endl; return 0; }
3、总结
3.1 优点
享元模式是一个非常简单的模式, 它可以大大减少应用程序创建的对象, 降低程序内存的占用, 增强程序的性能。
3.2 缺点
提高了系统复杂性, 需要分离出外部状态和内部状态, 而且外部状态具有固化特性, 不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。
3.3 使用场景
● 系统中存在大量的相似对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用。
● 细粒度的对象都具备较接近的外部状态, 而且内部状态与环境无关, 也就是说对象没有特定身份。
● 需要缓冲池的场景。
3.4 注意事项
Flyweight的内部状态是用来共享的,Flyweightfactory负责维护一个Flyweight池(存放内部状态的对象),当客户端请求一个共享Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池为重复或可共享的对象、属性设置一个缓冲,称为内部状态。这些内部状态一般情况下都是不可修改的,也就是在第一个对象、属性被创建后,就不会去修改了(否则就没意义了)。
Flyweight 对对象的内部状态进行共享,只为每种内部状态创建一个实例,对内部状态使用了单例模式。
用户不应直接对ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。