Pluggable Factories

So in my last article I touched on the subject of what I termed static factories. I did a little searching online and found the original article that first gave me the idea.  Apparently this type of design is called pluggable factories.  A term I had forgotten until just now.

Here is the basic idea, you have x number of objects your program can create.  These objects are all children of a base interface.  You have cleverly created an abstract factory to create the various instances of your objects, but there is a problem, you are asked to add several new objects and/or remove several objects.  So you jump back into your code and make a new object or remove an object.  You compile your code and try to use the new object, OOPS, you forgot to add it to the abstract factory!   Or, after removing the object you run some old data and OOPS, your program crashes as it tried to return an instance to a class that does not exist.

To be fair, if your program was written correctly it would not compile in the latter case, but that is besides the point.  Sometimes abstract factories can be a pain in the ass.  So, here is where pluggable factories step up to the plate.

They offer you the ability to create or remove objects without modifying any part of the project.  All you, as the programmer, need to do is create a new cpp with two well defined classes and you can use your new object.  How does this work?  It relies on a very simple but powerful aftereffect of static objects.

See, the basic idea of a static object is that only one exists.  It gets setup at the start of your program and never moves or changes.  If it is a method, it gets a special place in memory and any children of your class call that exact function.  If it is a variable, it gets initiated at the start of the program and all references to get sent directly to that location, no mater where in the program you are.

The idea behind pluggable factories is that you silently initiate several classes that only exist to make one specific type of object.  These classes are known as makers, and they are the key to pluggable factories.

Makers come in many different flavors and styles, but we will focus on one type for now.  Let us define an object derived by a common interface.

class Controller_Letter : public IController
{
    friend class Letter_Maker;
public:
    ~Controller_Letter() {}

    int fitness( void* data )
    {
    if( (char)data == _letter || (char)data == toupper(_letter) )
        return 1;
    return 0;
    }
    void perform() { printf( "%c", _letter ); }

protected:
    Controller_Letter() {}

private:
    void setLetter( char letter ) { _letter = letter; }
    char _letter;
};

Now this is a basic controller class for a project of mind, so do not pay to much attention to the methods. From the basic idea of this controller, I would want at least 26 of these classes, but to be safe I will instead make 255 of them. Can you imagine a switch statement with 255 cases?

To make our lives easier we will construt a pluggable factory to automatically create each class.  A pluggable factory is made of three parts, a std::map, a constructor, and a pure virtual make method.  The map will store our database of makers, the constructor will be used by our children to register with the database, and the virtual method will be defined by our children so they can make their object.  Lets take a look at a basic parent maker class

class Controller_Maker
{
public:
    static IController* newController( const std::string& type, void* data );
private:
    typedef std::map<std::string, Controller_Maker*> maker_map;

    static maker_map& getReg()
    {
        static maker_map registry;
        return registry;
    }
    Controller_Maker() {}
protected:
    Controller_Maker( const std::string& type )
    {
        getReg().insert( std::make_pair( type, this ) );
    }
    virtual IController* make( void* data ) const = 0;
};

We have defined all three parts, but there is something I would like to point out to everyone.  Notice the getReg function.  It would make sense to have our registry variable static inside the class, then have the children directly access it to register themselves, HOWEVER, here is the problem.  When your program gets compiled all your static elements are juggled by the linker and eventually end up sitting in an initialization list somewhere in your binary.  Among these variables are your child makers trying to register themselves in the registry.  Imagine what would happen if a child is initialized before the registry map is.  BOOM CRASH BANG, yep, you get a splendid show of fireworks before your program disintegrates back into the void.  The getReg function is how we avoid this very nasty problem.  See, functions do not need to be setup before hand, a static function is just an address, it can be called at any time.  Therefore we put the registry inside a static function, and we can guarantee that it gets initialized before a child tries to use it, because when a child uses it they initialize it.  Simple eh?

So now you have a maker, an interface, and an object you want many many variations of.  How do you define the individual makers to first register with the parent maker, and second, create the desired object?  Child makers will, as you may expect, define the make function from the parent maker and call the parents constructor.  However the key to child makers are the static instances of themselves they define.  It may sound weird, but each child defines a variable inside itself of itself.  This variable is initiated at the start of the program ( because its static, remember? ) and thus the child is registered with the parent.  Here is the child maker

class Letter_Maker : public Controller_Maker
{
protected:
    IController* make( void* data ) const;
private:
    Letter_Maker() : Controller_Maker( "letter" ) {}
    static const Letter_Maker registerThis;
};

Notice the constructor, it calls the parent with the type of object it makes, thus registered a new type of object with the pluggable factory.  Also key here is the registerThis variable. The only purpose of this variable is to register the class without modifying any factory or project source.  This class is naturally defined with the class it creates, this way you just add a cpp to the project and you have a new object.  Impressed yet?  Lets dive a bit further.

If you have been paying attention you have noticed that I have not defined some key methods, namely, Controller_Maker::newController or Letter_Maker::make.  Well it does not take much imagination to define these, here they are

IController* Letter_Maker::make( void* data ) const
{
    Controller_Letter* ret = new Controller_Letter();
    ret->setLetter( (char)data );  // Sending NULL will not hurt anything
    return ret;
}

IController* Controller_Maker::newController( const std::string& type, void* data )
{
    maker_map::iterator itr = getReg().find( type );
    if( itr != getReg().end() )
        return itr->second->make( data );
    return NULL;
}

I usually keep these methods separate from the header files for neatness sake.  Here is the quick run down on these.  Letter_Maker::make will make the object it was build to make.  Think of it as a small builder ( if you have read Design Patterns ).  Its important to not fall into temptation and pass void* data to the new object.  Notice how I create the object then call its setLetter method?  The maker data should stay independent of the object definition.  Remember DRY?

Controller_Maker::newController is just what you probably expected, it simply searches the registry for the type of maker we ask for and calls the specific maker’s make function.

In the end, we are left with a nice set of classes that can easily encapsulate all 255 acsii characters in just 255 lines ( if someone wants to add up the maker and controller class lines be my guest ).

Here is how we use the maker

IController* a = Controller_Maker::newController( "letter", (void*)'a' );
IController* b = Controller_Maker::newController( "letter", (void*)'b' );
IController* c = Controller_Maker::newController( "letter", (void*)'c' );
IController* d = Controller_Maker::newController( "letter", (void*)'d' );
IController* e = Controller_Maker::newController( "letter", (void*)'e' );
IController* f = Controller_Maker::newController( "letter", (void*)'f' );

Keep in mind that is by far the simplest way to use pluggable factories.  For more examples you can check out bafprp’s source code, pluggable factories are used for creating fields, records, and the most interesting of all is the output class, where I merged the maker and the object into one class.  And that is not all as far as I know I have barely even scratched the surface of pluggable factory design, so if you come up with a new technique, feel free to post!

Project source code here!

Tags: , , , ,

1 comment

Comments are now closed.

Charles Solar is Stephen Fry proof thanks to caching by WP Super Cache