Back to Design Patterns Study Group page

An Example of the Flyweight Pattern (p 195)

submitted by Ralph Richard Cook

Intent

Use sharing to support large numbers of fine-grained objects efficiently.

In my Example of the Strategy Design Pattern, I showed each Strategy context, CEditable, creating a new instance of the Strategy class to use. However, each Strategy has no instance data of its own - all the data it needs is passed in, either as members of the CEditable or as function parameters. Also, on some of the data entry views there could be thousands of CEditables. Therefore, I implemented the Strategies, the CEditor class and its subclasses, as Flyweights.

Referring to the Strategy example:

The Flyweight is the CEditor abstract class. All the states needed to function are extrinsic and passed in as function parameters.

The ConcreteFlyweights are the subclasses of CEditor (CStatusEditor, CParameterEditor, etc.).

The Client is the CEditable class. It either stores extrinsic state for the CEditor, or passes function parameters through from functions that call CEditable functions.

The FlyweightFactory, EditorFactory, is the only new class needed. Each Strategy is fundamentally different from the others; there is no common "CreateCharacter" function. Each CEditor-derived class has its own factory function.

class CEditorFactory {
   CStatusEditor *m_pStatusEditor;
   CParameterEditor *m_pParameterEditor;
   //...
};

CParameterEditor *CEditorFactory::GetParameterEditor()
{
   if (m_pParameterEditor == NULL)
      m_pParameterEditor = new CParameterEditor();
      return m_pParameterEditor;
   }
}

// Similar functions for the other CEditors
// ...

So, the code that was originally

   //...
   CEditor *peditor;
   CEditable *pe;
   pe = new CEditable();
   if (pe->GetType() == STATUS)
      peditor = new CStatusEditor();
   else if (pe->GetType() == BINARYPARAMETER)
      peditor = new CBinaryParameterEditor();
   else
      peditor = new CParameterEditor();
   pe->SetEditor(peditor);
   //...

becomes

   //...
   CEditor *peditor;
   CEditable *pe;
   pe = new CEditable();
   if (pe->GetType() == STATUS)
      peditor = m_EditorFactory.GetStatusEditor();
   else if (pe->GetType() == BINARYPARAMETER)
      peditor = m_EditorFactory.GetBinaryParameterEditor();
   else
      peditor = m_EditorFactory.GetParameterEditor();
   pe->SetEditor(peditor);
   //...

I had a tough time deciding where the CEditorFactory would "live". If it was a global variable (or, for you MFCheads, a member of my CWinApp-derived class) there would be less instantiations, but in my opinion would disrupt the encapsulation I was trying to achieve. Only the data entry view and "below" should know about the CEditors and such. I decided to make the a CEditorFactory instance a member of my CScreenParser class, which is a member of my data entry view. While this means new Flyweights are created every time a screen is parsed, it doesn't happen that often, and so far I don't think there will be more than eight or ten Strategies in use, so the performance hit is negligible compared to the potential loss of program cohesion.