程序设计模式(十一) C++ 享元(Flyweight)模式

来源:互联网 发布:移动网络被断 编辑:程序博客网 时间:2024/06/17 14:46

2.6  Flyweight

想想我们编辑文档用的wps,文档里文字很多都是重复的,我们不可能为每一个出现的汉字都创建独立的空间,这样代价太大,最好

的办法就是共享其中相同的部分,使得需要创建的对象降到最小,这个就是享元模式的核心,即运用共享技术有效地支持大量细粒度

的对象。

享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。內部状态是存储在享元对象内部并且不会

随环境改变而改变。因此内部状态并可以共享。

外状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使

用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。在Flyweight模式应用中,通常修改的是外部状态属性,而内

部状态属性一般都是用于参考或计算时引用。Flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于

ConcreteFlyweight对象之中;而外部状态则由Client对象存储或计算。当用户调用Flyweight对象的操作时,将该状态传递给它。以

文字处理软件为例:内部状态存储于flyweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享。如字符

代码,字符大小……  外部状态取决于flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态

传递给flyweight,如字符位置,字符颜色……



Flyweight:享元类的基类,定义一个接口,通过这个接口Flyweight可以接受并作用于外部状态。


ConcreteFlyweight:实现Flyweight接口, 并为内部状态( 如果有的话) 增加存储空间。ConcreteFlyweight对象必须是可共享

的。它所存储的状态必须是内部的(intrinsic);即,它必须独立于ConcreteFlyweight对象的场景。

UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。在

Flyweight对象结构的某些层次,UnsharedConcreteFlyweight对象通常将ConcreteFlyweight对象作为子节点。

FlyweightFactory

      1) 创建并管理Flyweight对象。

      2)确保合理地共享Flyweight。当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)

Client
      1)维持一个对Flyweight的引用。

      2)计算或存储一个(多个)Flyweight的外部状态。

思想:Flyweight可能有的朋友第一次看到想象不到是什么样子,其实说他就是一个Pool,你可能就明白了。也就是由一个

FlyweightFactory来管理一族一定数目逻辑上经常需要构建和销毁的细颗粒对象,例如我们常见的数据库连接池。在Factory内部,并

不物理销毁这些对象,而在接到实例化请求时返回这些被关系对象的实例,从而减少创建销毁这些细颗粒对象的开销。实现要点如

下:

1、面向对象很好的解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模

式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。

2Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意

对象状态的处理。

3、享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:享元模式使得系统更加

复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。另外它将享元对象的状态外部化,而读取外部状态

使得运行时间稍微变长。

享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:

1、享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。

2、享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长


场景:基本上所有的需要Pool这个概念的环境都能应用。当以下所有的条件都满足时,可以考虑使用享元模式:

1、一个系统有大量的对象。 

2、这些对象耗费大量的内存。 

3、这些对象的状态中的大部分都可以外部化。 

4、这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。 

5、软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。

满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资

源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式

实现:实现的底层方式可以千变万化,在接口上就是如上图所示,花样不多。

1)享元工厂维护一张享元实例表。

2)享元不可共享的状态需要在外部维护。即删除外部状态:该模式的可用性在很大程度上取决于是否容易识别外部状态并将它从共

享对象中删除。

3)按照需求可以对享元角色进行抽象。

4)管理共享对象:引用计数和垃圾回收……

实例:

在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费。例如一个

字母“a”在文档中出现了100000次,而实际上我们可以让这一万个字母“a”共享一个对象,当然因为在不同的位置可能字母“a”有不同

的显示效果(例如字体和大小等设置不同),在这种情况我们可以为将对象的状态分为“外部状态”和“内部状态”,将可以被共享(不

会变化)的状态作为内部状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适当的时候将外部对象最为参

数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。

Flyweight的内部状态是用来共享的,Flyweightfactory负责维护一个Flyweight池(存放内部状态的对象),当客户端请求一个共享

Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到

池中,再返回送出这个对象.池为重复或可共享的对象、属性设置一个缓冲,称为内部状态。这些内部状态一般情况下都是不可修改

的,也就是在第一个对象、属性被创建后,就不会去修改了(否则就没意义了)。

Flyweight 对对象的内部状态进行共享,只为每种内部状态创建一个实例,对内部状态使用了单例模式。用户不应直接对

ConcreteFlyweight类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享。

存储节约由以下几个因素决定:

  1) 因为共享,实例总数减少的数目

  2) 对象内部状态的平均数目

  3) 外部状态是计算的还是存储的

在文档编辑器例子中如果一个字符对应一个对象,那么一篇文档所要容纳的对象将是非常的庞大耗费大量的内存。而实际组成文档的

字符是有限的,是由这些字符不同的组合和排列得到的。所以需要共享,将基本的字符进行共享,将使得字符对象变得有限。

代码如下:

Flyweight.h

 1 #ifndef _FLYWEIGHT_H_ 2 #define _FLYWEIGHT_H_ 3  4 #include <string> 5 #include <vector> 6  7 using namespace std; 8  9 //基类,定义操作接口Operation10 class Flyweight11 {12 public:13     //操作外部状态extrinsicState14     virtual void Operation(const string& extrinsicState)=0;15     string GetIntrinsicState();16     virtual ~Flyweight();17 protected:18     Flyweight(string intrinsicState);19 private:20     //内部状态,也可以放在ConcreteFlyweight中21     string _intrinsicState;22 };23 24 class ConcreteFlyweight:public Flyweight25 {26 public:27     //实现接口函数28     virtual void Operation(const string& extrinsicState);29     ConcreteFlyweight(string intrinsicState);30     ~ConcreteFlyweight();31 };32 33 class UnsharedConcreteFlyweight:public Flyweight34 {35 public:36     virtual void Operation(const string& extrinsicState);37     UnsharedConcreteFlyweight(string intrinsicState);38     ~UnsharedConcreteFlyweight();39 };40 41 class FlyweightFactory42 {43 public:44     FlyweightFactory();45     ~FlyweightFactory();46     //获得一个请求的Flyweight对象47     Flyweight* GetFlyweight(string key);48     //获取容器中存储的对象数量49     void GetFlyweightCount();50 protected:51 private:52     //保存内部状态对象的容器53     vector<Flyweight*> m_vecFly;54 };55 #endif

Flyweight.cpp

 1 #include "Flyweight.h" 2 #include <iostream> 3  4 using namespace std; 5  6 Flyweight::Flyweight(string intrinsicState) 7 { 8     this->_intrinsicState = intrinsicState; 9 }10 11 Flyweight::~Flyweight()12 {}13 14 string Flyweight::GetIntrinsicState()15 {16     return this->_intrinsicState;17 }18 19 ConcreteFlyweight::ConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)20 {21 }22 23 ConcreteFlyweight::~ConcreteFlyweight()24 {25 }26 27 void ConcreteFlyweight::Operation(const string& extrinsicState)28 {29     cout << this->GetIntrinsicState() << endl;30     cout << extrinsicState << endl;31 }32 33 UnsharedConcreteFlyweight::UnsharedConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)34 {35 }36 37 UnsharedConcreteFlyweight::~UnsharedConcreteFlyweight()38 {39 }40 41 void UnsharedConcreteFlyweight::Operation(const string& extrinsicState)42 {43     cout << "extrinsicState" << endl;44 }45 46 FlyweightFactory::FlyweightFactory()47 {}48 49 FlyweightFactory::~FlyweightFactory()50 {}51 52 //若该对象已存在,直接返回,否则新建一个对象,存入容器中,再返回53 Flyweight* FlyweightFactory::GetFlyweight(string key)54 {55     vector<Flyweight*>::iterator iter = this->m_vecFly.begin();56     for(;iter!= this->m_vecFly.end();iter++)57     {58         if((*iter)->GetIntrinsicState() == key)59         {60             return *iter;61         }62     }63     Flyweight* fly = new ConcreteFlyweight(key);64     this->m_vecFly.push_back(fly);65     return fly;66 }67 68 void FlyweightFactory::GetFlyweightCount()69 {70     cout << this->m_vecFly.size() << endl;71 }

main.cpp

 1 #include "Flyweight.h" 2 #include <iostream> 3 #include <string> 4  5 using namespace std; 6  7 int main() 8 { 9     //外部状态extrinsicState10     string extrinsicState = "ext";11 12     //工厂对象,工厂对象13     FlyweightFactory* fc = new FlyweightFactory();14 15     //向工厂申请一个Flyweight对象,且该对象的内部状态值为“hello”16     Flyweight* fly = fc->GetFlyweight("hello");17 18     Flyweight* fly1 = fc->GetFlyweight("hello");19 20     //应用外部状态21     fly->Operation(extrinsicState);22 23     fc->GetFlyweightCount();24 25     return 0;26 }

另外一个文本处理的例子

代码如下:

Document.h

 1 #ifndef _DOCUMENT_H_ 2 #define _DOCUMENT_H_ 3  4 #include <string> 5 #include <vector> 6  7 using namespace std; 8  9 // The 'Flyweight' abstract class10 class character11 {12 public:13     //析构函数14     virtual ~character();15     //应用外部状态16     virtual void Display(int width,int height,int ascent,int descent,int pointSize)=0;17     //获取内部状态18     virtual char GetSymbol()=0;19 protected:20     /*-----内部状态---------*/21     char symbol;22     /*----------------------/23 24     /*-----外部状态---------*/25     int width;26     int height;27     int ascent;28     int descent;29     int pointSize;30     /*----------------------*/31     //构造函数32     character(char c);33 };34 35 //A 'ConcreteFlyweight' class36 class characterA:public character37 {38 public:39     characterA(char c);40     ~characterA();41     virtual void Display(int width,int height,int ascent,int descent,int pointSize);42     virtual char GetSymbol();43 protected:44 private:45 };46 47 //B 'ConcreteFlyweight' class48 class characterB:public character49 {50 public:51     characterB(char c);52     ~characterB();53     virtual void Display(int width,int height,int ascent,int descent,int pointSize);54 protected:55 private:56 };57 58 //C 'ConcreteFlyweight' class59 class characterC:public character60 {61 public:62     characterC(char c);63     ~characterC();64     virtual void Display(int width,int height,int ascent,int descent,int pointSize);65 protected:66 private:67 };68 69 //D 'ConcreteFlyweight' class70 class characterD:public character71 {72 public:73     characterD(char c);74     ~characterD();75     virtual void Display(int width,int height,int ascent,int descent,int pointSize);76 protected:77 private:78 };79 /*80 ...81 */82 83 //The 'FlyweightFactory' class84 class characterFactory85 {86 public:87     characterFactory();88     ~characterFactory();89     //申请一个character对象90     character* GetCharacter(char);91     //获取存储的character*数量92     vector<character*>::size_type GetCount();93 private:94     //保存character*的容器,可以换成List等其它容器95     vector<character*> m_vecCharacter;96 };97 98 #endif

Document.cpp

 1 #include "Document.h" 2 #include <iostream> 3  4 character::character(char c) 5 { 6     this->symbol = c; 7 } 8  9 character::~character()10 {11 }12 13 characterA::characterA(char c):character(c)14 {15 }16 17 characterA::~characterA()18 {19 }20 21 char characterA::GetSymbol()22 {23     return this->symbol;24 }25 26 void characterA::Display(int width,int height,int ascent,int descent,int pointSize)27 {28     //接收并作用外部状态29     this->ascent = ascent;30     this->descent = descent;31     this->height = height;32     this->pointSize = pointSize;33     this->width = width;34 35     cout << this->symbol <<" "36      << this->ascent <<" "37      << this->descent <<" "38      << this->height <<" "39      << this->pointSize <<" "40      << this->width << endl;41 }42 43 characterFactory::characterFactory()44 {}45 46 characterFactory::~characterFactory()47 {}48 49 character* characterFactory::GetCharacter(char c)50 {51     vector<character*>::iterator iter = this->m_vecCharacter.begin();52     for(;iter != this->m_vecCharacter.end();iter++)53     {54         if((*iter)->GetSymbol() == c)55         {56             return *iter;57         }58     }59     character* pf = new characterA(c);60     this->m_vecCharacter.push_back(pf);61     return pf;62 }63 64 vector<character*>::size_type characterFactory::GetCount()65 {66     return this->m_vecCharacter.size();67 }

main.cpp

 1 #include "Flyweight.h" 2 #include "Document.h" 3 #include <iostream> 4 #include <string> 5  6 using namespace std; 7  8 int main() 9 {10     //存储外部状态11     int ascent = 70;12     int descent = 0;13     int height = 100;14     int width = 120;15     int pointSize = 10;16 17     string test = "AABCDDEFGHI";18     string::iterator it = test.begin();19     characterFactory* pcF = new characterFactory();20     for(;it!=test.end();it++)21     {22         pointSize++;23         char c = *it;24         //申请一个character对象25         character* charc= pcF->GetCharacter(c);26         //应用外部状态27         charc->Display(width,height,ascent,descent,pointSize);28     }29     vector<character*>::size_type sizeChar = pcF->GetCount();30     cout << "count:" << sizeChar << endl;31 32     return 0;33 }

重构成本:低。