我所理解的设计模式(C++实现)——享元模式(Flyweight Pattern)

来源:互联网 发布:用友软件客服电话 编辑:程序博客网 时间:2024/05/21 22:28

概述

想想我们编辑文档用的wps,文档里文字很多都是重复的,我们不可能为每一个出现的汉字都创建独立的空间,这样代价太大,最好的办法就是共享其中相同的部分,使得需要创建的对象降到最小,这个就是享元模式的核心,即运用共享技术有效地支持大量细粒度的对象。

享元对象能做到共享的关键是区分内蕴状态(Internal State)和外蕴状态(External State)。内蕴状态是存储在享元对象内部并且不会随环境改变而改变。因此内蕴状态并可以共享。

外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。

类图与样例


抽象享元类(Flyweight

它是所有具体享元类的超类。为这些类规定出需要实现的公共接口,那些需要外蕴状态(Exte的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

具体享元类(ConcreteFlyweight)

具体享元类实现了抽象享元类所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元类又称为单纯具体享元类,因为复合享元类是由单纯具体享元角色通过复合而成的。

不能共享的具体享元类(UnsharableFlyweight)

不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享。

享元工厂类(FlyweightFactoiy)

享元工厂类负责创建和管理享元对象。当一个客户端对象请求一个享元对象的时候,享元工厂需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。

客户类(Client)

客户类需要自行存储所有享元对象的外蕴状态。


[cpp] view plaincopy
  1. // CplusplusFlyweight.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <map>  
  7. using namespace std;  
  8. class Character    
  9. {  
  10. public:  
  11.     virtual ~Character(){};  
  12.   
  13.     virtual void SetSize(intint) = 0;  
  14.     virtual void Display() = 0;  
  15. protected:  
  16.     Character(){};  
  17.     char m_chSymbol;  
  18.     int m_nWeight;  
  19.     int m_nHeight;  
  20. };  
  21.   
  22. class CharacterA : public Character  
  23. {  
  24. public:  
  25.     CharacterA();  
  26.     virtual ~CharacterA();  
  27.   
  28.     void SetSize(intint);  
  29.     void Display();  
  30. };  
  31.   
  32. CharacterA::CharacterA()  
  33. {  
  34.     this->m_chSymbol = 'A';  
  35.     this->m_nWeight = 100;  
  36.     this->m_nHeight = 200;  
  37. }  
  38.   
  39. CharacterA::~CharacterA()  
  40. {  
  41.   
  42. }  
  43. void CharacterA::SetSize(int nWeight, int nHeight)  
  44. {  
  45.     this->m_nWeight = nWeight;  
  46.     this->m_nHeight = nHeight;  
  47. }  
  48. void CharacterA::Display()  
  49. {  
  50.     cout << "CharacterA:" << m_chSymbol << "(" << m_nWeight << "," << m_nHeight << ")" << endl;  
  51. }  
  52.   
  53. class CharacterB : public Character  
  54. {  
  55. public:  
  56.     CharacterB();  
  57.     virtual ~CharacterB();  
  58.   
  59.     void SetSize(intint);  
  60.     void Display();  
  61. };  
  62.   
  63. CharacterB::CharacterB()  
  64. {  
  65.     this->m_chSymbol = 'B';  
  66.     this->m_nWeight = 100;  
  67.     this->m_nHeight = 200;  
  68. }  
  69.   
  70. CharacterB::~CharacterB()  
  71. {  
  72.   
  73. }  
  74.   
  75. void CharacterB::SetSize(int nWeight, int nHeight)  
  76. {  
  77.     this->m_nWeight = nWeight;  
  78.     this->m_nHeight = nHeight;  
  79. }  
  80.   
  81. void CharacterB::Display()  
  82. {  
  83.     cout << "CharacterB:" << m_chSymbol << "(" << m_nWeight << "," << m_nHeight << ")" << endl;  
  84. }  
  85.   
  86. class CharacterFactory    
  87. {  
  88. public:  
  89.     CharacterFactory();  
  90.     virtual ~CharacterFactory();  
  91.   
  92.     Character* GetCharacter(char);  
  93. private:  
  94.     std::map<char, Character*> m_mChar;  
  95. };  
  96.   
  97. CharacterFactory::CharacterFactory()  
  98. {  
  99.     m_mChar.insert(make_pair<char, Character*>('A'new CharacterA));  
  100.     m_mChar.insert(make_pair<char, Character*>('B'new CharacterB));  
  101. }  
  102.   
  103. CharacterFactory::~CharacterFactory()  
  104. {  
  105.   
  106. }  
  107.   
  108. Character* CharacterFactory::GetCharacter(char chIn)  
  109. {  
  110.     map<char, Character*>::iterator it = m_mChar.find(chIn);  
  111.     if(it != m_mChar.end())  
  112.     {  
  113.         return (Character*)it->second;  
  114.     }  
  115.   
  116.     return NULL;  
  117. }  
  118.   
  119. int _tmain(int argc, _TCHAR* argv[])  
  120. {  
  121.     CharacterFactory* pFactory = new CharacterFactory;  
  122.   
  123.     //内蕴状态 存储在享元对象内部并且不会随环境改变而改变  
  124.     Character* ch1 = pFactory->GetCharacter('A');  
  125.     ch1->Display();  
  126.   
  127.     //外蕴状态 客户端保存  
  128.     Character* ch2 = pFactory->GetCharacter('B');  
  129.     ch2->SetSize(500, 800);  
  130.     ch2->Display();  
  131.     return 0;  
  132. }  

要点

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

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

3、享元模式的优点在于它大幅度地降低内存中对象的数量。但是,它做到这一点所付出的代价也是很高的:享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。另外它将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

适用性

当以下所有的条件都满足时,可以考虑使用享元模式:

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

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

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

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

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

满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。

优缺点

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

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

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

0 0
原创粉丝点击