Use sharing to allow large number of objects to be used efficiently.
A flyweight is an object that can be used independently in many contexts simultaneously. Flyweights make no assumption about the context in which they operate. A flyweight cannot be distinguished from an unshareable instance of the same object. The key concept in Flyweights is the use of extrinsic and intrinsic state; Intrinsic state, which is stored inside the object, is independent of the flyweight's context. Extrinsic state, which is maintained by Flyweight clients, depend on the flyweight's context.
Clients obtains a concrete flyweight exclusively from a flyweight factory object. The factory ensures that concrete objects are shared properly. Note that clients should never instantiate concrete flyweights directly.
An electronics circuit might consist of tens or hundreds of electrical components. A simulation/schematic program (skeleton below) might use the Flyweight pattern to allow sharing of these electrical components (Resistors, Capacitors, etc.) in order to build complex circuits. The intrinsic state might include the element's value, whereas the extrinsic state might include the element's location on a grid and/or its connection to other elements.
/* Flyweight Classes */
/* Flyweight: Declares an interface through which flyweight receive and act on
extrinsic state */
class CElectronicFlyweightComponent
{
// Constructors/Destructor
public:
virtual ~CElectronicFlyweightComponent() {}
// Public interface
public:
// sets extrinsic state for concrete flyweights
virtual void Draw( const std::pair<int, int>& ) = 0;
// ...
};
/* Concrete Flyweights: Implement the flyweight interface and add intrinsic state, if any.
Since a flyweight must be shareable, its intrinsic state must be independent of the flyweight's
context. In this example, there are two electronic components implemented as flyweights,
Resistor and Capacitor */
class Resistor : public CElectronicFlyweightComponent
{
// Constructor/Destructor
public:
Resistor( const double dRes = 0) : m_Resistance(dRes) {}
~Resistor(){}
// Public interface
public:
// sets extrinsic state for concrete
flyweights
void Draw( const std::pair<int, int>& loc) { // ...
}
// required for an STL component
bool operator < ( const Resistor& rhs) const { return (m_Resistance < rhs.m_Resistance); }
// data members
public:
/* intrinsic data */
const double m_Resistance;
};
class Capacitor : public CElectronicFlyweightComponent
{
// Constructor/Destructor
public:
Capacitor( const double dCap = 0) : m_Capacitance(dCap) {}
~Capacitor(){}
// Public interface
public:
// sets extrinsic state for concrete flyweights
void Draw( const std::pair<int, int>& loc) {
// ... }
// required for an STL component
bool operator<( const Capacitor& rhs) const { return (m_Capacitance < rhs.m_Capacitance); }
// data members
public:
/* intrinsic data */
const double m_Capacitance;
};
/* Flyweight Factory */
/* Flyweight Factory: Creates and manages flyweight objects. It also
ensures that flyweights are managed properly by supplying an existing instance or creating a new one, if none exists. In this
example, CElectronicFlyweightComponent creates and manages access to CResistor,
and CCapacitor */
class CElectronicFlyweightComponentFactory
{
typedef std::set<Resistor*> SET_RESISTOR;
typedef std::set<Capacitor*> SET_CAPACTIOR;
// Constructors/Destructor
public:
CElectronicFlyweightComponentFactory(){}
~CElectronicFlyweightComponentFactory()
{
// Delete all elements in the
member sets
std::for_each( m_setResistors.begin(),
m_setResistors.end(),
DeleteElement<Resistor*>()
);
std::for_each( m_setCapcitors.begin(),
m_setCapcitors.end(),
DeleteElement<Capacitor*>()
);
}
// Public operations
public:
Resistor* GetResistor( const double dRes );
Capacitor* GetCapacitor( const double dCap );
// ...
// Data members
private:
SET_RESISTOR m_setResistors;
SET_CAPACTIOR m_setCapcitors;
};
/* Return an existing Resistor if one exists. Else return a new one.
Note on using pointer versus object as container element: Using as pointer makes it difficult to search
for a given resistor element. We need to iterator over each element and compare its m_Resistance to the given
value. Using an object poses the following questions: Can we safely return the address
of elements in the set? Depends on the container and whether it invalidates its
iterators after an insert or erase */
Resistor* CElectronicFlyweightComponentFactory::GetResistor( const double dRes )
{
// Lookup a resistor with the given impedance
SET_RESISTOR::iterator pos;
pos = std::find_if( m_setResistors.begin(),
m_setResistors.end(),
ResistorValue<SET_RESISTOR::value_type, double>( dRes)
);
Resistor* pRes = NULL;
if (pos != m_setResistors.end())
{
// element found.
Get it.
pRes = *(pos);
}
else
{
// Element not found. Insert
a new one and return it
pRes = new Resistor( dRes );
if ( !(m_setResistors.insert( pRes ).second) )
{
delete pRes;
throw std::runtime_error( "Failed to insert resisitor" );
}
}
return pRes;
}
Capacitor* CElectronicFlyweightComponentFactory::GetCapacitor( const double dCap )
{
// Lookup a capacitor with the given impedance
SET_CAPACTIOR::iterator pos;
pos = std::find_if( m_setCapcitors.begin(),
m_setCapcitors.end(),
CapacitorValue<SET_CAPACTIOR::value_type, double>( dCap)
);
Capacitor* pCap = NULL;
if (pos != m_setCapcitors.end())
{
// element found.
Get it.
pCap = *(pos);
}
else
{
// Element not found. Insert
a new one and return it
pCap = new Capacitor( dCap );
if (! (m_setCapcitors.insert( pCap ).second) )
{
delete pCap;
throw std::runtime_error( "Failed to insert capacirtor" );
}
}
return pCap;
}
/* Helper functors for CElectronicFlyweightComponentFactory
class */
// Deletes the current element (used in std::for_each)
template<typename T>
class DeleteElement
{
public:
operator()( T pT) { delete pT; pT = NULL; }
};
// Checks the current Resistor against a given value (used
in std::find_if)
template<typename U, typename V>
class ResistorValue
{
private:
V Value; //
Resistor value to search against
public:
ResistorValue( const V v = V() ) : Value(v) {}
bool operator()( U elem ) { return (elem->m_Resistance == Value);
}
};
// Checks the current Capacitor against a given value
(used in std::find_if)
template<typename U, typename V>
class CapacitorValue
{
private:
V Value; // Capacitor
value to search against
public:
CapacitorValue( const V v = V() ) : Value(v) {}
bool operator()( U elem ) { return (elem->m_Capacitance == Value);}
};
int main(int argc, char* argv[])
{
try
{
CElectronicFlyweightComponentFactory obFactory;
// Create a Resistor with value of 10 ohms
Resistor* pobR1 = obFactory.GetResistor( 10.0 );
pobR1->Draw( std::make_pair<int,int>(1,1) );
// Create a Resistor with value of 11 ohms
Resistor* pobR2 = obFactory.GetResistor( 11.0 );
pobR2->Draw( std::make_pair<int,int>(1,2) );
// Create a Resistor with value of 10. Note that the Flyweight will return
// an existing Resistor with the same context-independent intrinsic state
Resistor* pobR3 = obFactory.GetResistor( 10.0 );
// Same os pobR1
pobR3->Draw( std::make_pair<int,int>(1,3) );
}
catch( const std::exception& e )
{
std::cout << e.what() << std::endl;
}
return 0;
}