C++实现Structural - Flyweight模式

 

面向对象很好地解决了系统抽象性的问题,同时在大多数情况下,也不会损及系统的性能。但是,在某些特殊的应用中,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销。比如图形应用中的图元等对象、字处理应用中的字符对象等等。

 

采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价---主要指内存需求方面的代价。

Flyweight(享元)设计模式可以在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作。

The Flyweight Design Pattern is useful when there is the need for many, many objects to exist that share some information. Several thousand or even several hundred thousand objects might be needed, and this is usually very memory-consuming to keep track of. Given certain constraints, it is possible to reduce this problem greatly and make the presence of so many objects possible. If all of the objects share some intrinsic, invariant information that is constant among all of them, it can be removed from each object, and referenced. This eliminates the redundancy of having to repeat the same invariant, intrinsic information for each object, so instead of storing the same informationn times for n objects, it is only stored once. This object that contains all of the intrinsic information is called a flyweight object.

It is possible for a flyweight to have extrinsic information as well. This information must be stateless and determined by context, having no stored values, but values that can be calculated on the spot. This separation into extrinsic and intrinsic information allows great numbers of similar objects to exist, differing only in the context in which they exist.

Use sharing to support large numbers of fine-grained objects efficiently (运用共享技术有效地支持大量细粒度的对象)

--- GoF

 

Flyweight设计模式静态UML类图:

 

Flyweight:

The interface(or abstract class) defines the methods clients can use to pass external state into the flyweight objects.

 

ConcreteFlyweight:

This implements the Flyweight interface(or abstract class), and implements the ability to store internal data. The internal data has to be representative for all the instances where you need the Flyweight.ConcreteFlyweight对象就是可以共享的对象,是接口的具体实现,并为内部状态增加内存空间,它保存的任何状态都必须是内部(intrinsic),也就是说,ConcreteFlyweight必须和它的应用环境场合无关。

 

UnsharedConcreteFlyweight:

并不是所有的Flyweight具体实现子类都需要被共享的,还有另外一种不共享的ConcreteFlyweight。Not all Flyweight subclasses need to be shared. The Flyweight interface enables sharing, but it doesn't enforce it. It is common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the flyweight object structure. 正因为如此,在实际应用中UnsharedConcreteFlyweight这个类几乎不使用。在这种情况下,Flyweight类或其子类中,extrinsicState将作为数据成员存在(见上图的解释)

 

FlyweightFactory:

This factory is responsible for creating and managing the Flyweights. Providing access to Flyweight creation through the factory ensures proper sharing. The factory can create all the flyweights at the start of the application, or wait until they are needed.

 

几乎在所有情况下,Flyweight都是一个Singleton对象,关于这点可以从下面的代码中得知。进一步地,在几乎所有的应用中,FlyweightFactory也是一个Singleton。

 

示例代码:

// Flyweight.h

#include <string>

#include <map>

#include <vector>

#include <iostream>

using namespace std;

 

class AbstractFont      // Flyweight抽象类

{

protected:

         string font_name;      // IntrinsicState,即内部状态

 

public:

         virtual void set_font_name(string font_name) = 0;    // operation

         virtual string get_font_name() = 0;                               

         virtual string display() = 0;

public:

         virtual ~AbstractFont()

         {

                   cout << "in the destructor of AbstractFont..." << endl;

         }

};

 

class SongTiFont : public AbstractFont      // ConcreteFlyweight

{

public:

         SongTiFont(string font_name)

         {

                   this->font_name = font_name;

         }

 

         string display()

         {

                   return font_name;

         }

 

         string get_font_name()

         {

                   return font_name;

         }

 

         void set_font_name(string font_name)

         {

                   this->font_name = font_name;

         }

 

public:

         ~SongTiFont()

         {

                   cout << "in the destructor of SongTiFont..." << endl;

         }

};

 

class FontFactory      // FlyweightFactory

{

private:

         map<string, AbstractFont*> fonts;      // 这就是pool of flyweights

 

public:

         AbstractFont* get_font(string font_name)      // GetFlyweight,font_name就是key

         {

                   AbstractFont *font;

                   // 根据key font_name查找对应的Flyweight

                   map<string, AbstractFont*>::iterator it = fonts.find(font_name);

 

                   if(it == fonts.end())      // 如果要找的Flyweight不存在

                   {

                            font = new SongTiFont(font_name);       // 创建一个新的Flyweight

                            fonts.insert(pair<string, AbstractFont*>(font_name, font));       // 加入到pool of flyweights

                   }

                   else // 如果要找的Flyweight已经存在

                   {

                            font = it->second;

                   }

 

                   return font; // 返回Flyweight

         }

 

         size_t get_number()

         {

                   return fonts.size();

         }

 

public:

         ~FontFactory()

         {

                   cout << "in the destructor of FontFactory..." << endl;

                   for(map<string, AbstractFont*>::iterator iter = fonts.begin(); iter != fonts.end(); )

                   {

                            delete iter->second;      // 在STL中,如果容器中装载的是指针而非值对象,则内存回收

                            iter = fonts.erase(iter);       // 的工作,要程序员自己完成。

                            //iter--;

             }

         }

};

 

class FontedCharacter      // 这是一个应用类

{

private:

         string character;

         AbstractFont *font;

 

public:

         FontedCharacter()    // 之所以要有缺省构造函数,因为Flyweight.cpp中,FontedCharacter chars[10];

         {                                                                        

         }

 

         FontedCharacter(string character, AbstractFont *font)

         {

                   this->character = character;

                   this->font = font;

         }

 

         string set_font()

         {

                   return character + " - " + font->display();

         }

 

public:

         ~FontedCharacter()

         {

                   cout << "in the destructor of FontedCharacter..." << endl;

         }

};

 

// Flyweight.cpp

#include "Flyweight.h"

 

int main(int argc, char **argv)

{

         FontFactory *ff = new FontFactory();

 

         FontedCharacter chars[10];

 

         chars[0] = FontedCharacter("A", ff->get_font("宋体红色"));      // 1

         chars[1] = FontedCharacter("B", ff->get_font("宋体红色"));     // 2

         chars[2] = FontedCharacter("C", ff->get_font("宋体红色"));     // 3

         chars[3] = FontedCharacter("D", ff->get_font("宋体红色"));     // 4

         chars[4] = FontedCharacter("E", ff->get_font("宋体红色"));      // 5

         chars[5] = FontedCharacter("F", ff->get_font("宋体黑色"));       // 6

         chars[6] = FontedCharacter("G", ff->get_font("宋体黑色"));       // 7

         chars[7] = FontedCharacter("H", ff->get_font("宋体黑色"));       // 8

         chars[8] = FontedCharacter("I", ff->get_font("宋体黑色"));         // 9

         chars[9] = FontedCharacter("J", ff->get_font("宋体黑色"));        // 10

 

         cout << endl;

         for(int i = 0; i < 10; i++)

         {

                   cout << chars[i].set_font() << endl;

         }

 

         cout << "\n实际有" << ff->get_number() << " 种字体。" << endl;

 

         delete ff;

 

         return 0;

}

 

运行结果:

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

 

A - 宋体10红色

B - 宋体10红色

C - 宋体10红色

D - 宋体10红色

E - 宋体10红色

F - 宋体10黑色

G - 宋体10黑色

H - 宋体10黑色

I - 宋体10黑色

J - 宋体10黑色

 

实际有 2 种字体。

in the destructor of FontFactory...

in the destructor of SongTiFont...                                   // 第一种字体的销毁

in the destructor of AbstractFont...

in the destructor of SongTiFont...                                   // 第二种字体的销毁

in the destructor of AbstractFont...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

in the destructor of FontedCharacter...

 

运行结果中前10条输出,是由Flyweight.cpp中的1到10语句销毁临时FontedCharacter产生的。

 

为什么使用Flyweight能够节省内存?

只要AbstractFont的子类,如SongTiFont对象的大小超过4Bytes (即在32-bit操作系统中,指针需要的字节数),那么使用Flyweight就可以节省内存空间。在实际应用开发中,AbstractFont的子类对象的大小很容易超过4bytes的,如上如例子中的SongTiFont对象至少有12Bytes(不计其他内存消耗,“宋体11黄色”是以Unicode方式存储的,每个字符占两个字节,共12个字节),如果不使用Flyweight,那么10个字符在字体设置上需要:12 * 10 = 120bytes;使用Flyweight后,10个字符在字体设置上需要:

4 * 10 + 12 * 2 = 64bytes,其中的4是指针需要的,共有10个字符,因此 4 * 10, 另外,12是字体实际内容(如“宋体10红色”或者“宋体11黄色”)需要的,有两种不同的字体,因此12 * 2。所以,通过使用Flyweight,上面程序节省了120 – 64 = 56bytes。

如果像上面的FontedCharacter这样的小对象有很多,那么节省的空间就很可观了。

你可能感兴趣的:(C++,String,iterator,character,interface,destructor)