Structural

Flyweight

Purpose

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.

UML

Behavior

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.

Example

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.

Usage

/* 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;
}

Notes