9
\$\begingroup\$

For my project, I needed a way to instantiate different object types during runtime using "string names", for this I designed a generic factory that is created for each object hierarchy type (currently there are two different hierarchies that need the factory).

We want the factory to be simple to use and simple to register new types, for this, the factory relies on two classes types.

The first one, is the object creator, that each new class that needs to register on the factory instantiates, this is constructed as shown below:

template <typename T, typename Y>
class ObjectCreatorBase_c: public ObjectCreatorAutoUnlinkHook_t
{
    public:
        typedef T ObjectType_t;
        typedef Y ObjectCreatorProc_t;

     public:
        ObjectCreatorBase_c(const String_c &name, ObjectCreatorProc_t proc):
            strName(name),
            pfnCreateProc(proc)
        {
            if(proc == NULL)
            {
                std::stringstream stream;
                stream << "creator proc cant be null, entity " << name;
                PH_RAISE(INVALID_PARAMETER_EXCEPTION, "[ObjectCreatorBase_c::ObjectCreatorBase_c]", stream.str());
            }
        }

        T Create(const String_c &name) const
        {
            return pfnCreateProc(name);
        }

        inline const String_c &GetName() const
        {
            return strName;
        }

        inline bool operator<(const ObjectCreatorBase_c &rhs) const
        {
            return strName.compare(rhs.strName) < 0;
        }

    private:
        String_c strName;

    protected:
        ObjectCreatorProc_t pfnCreateProc;
};

This base class is created because there are two types (right now) of creation function, one with a single parameter for the constructor and another with two. Each type is specialized as shown below:

template <typename T>
class ObjectCreator_c: public ObjectCreatorBase_c<T, T(*)(const String_c &)>
{
    public:                     
        typedef ObjectCreatorBase_c<T, ObjectCreatorProc_t> BaseType_t;

    public:
        ObjectCreator_c(const String_c &name, ObjectCreatorProc_t proc):
            BaseType_t(name, proc)              
        {               
            GenericFactory_c<ObjectCreator_c<T> >::GetInstance().Register(*this);
        }
};

template <typename T, typename Y>
class ObjectCreator1_c: public ObjectCreatorBase_c<T, T(*)(const String_c &, Y )>
{       
    public:
        ObjectCreator1_c(const String_c &name,  T(*proc)(const String_c &, Y ) ):
            ObjectCreatorBase_c(name, proc)             
        {               
            GenericFactory1_c<ObjectCreator1_c, Y >::GetInstance().Register(*this);
        }

        T Create(const String_c &name, Y param) const
        {
            return pfnCreateProc(name, param);
        }
};

The first creator just has a default string parameter that is common for all classes and the second one has an extra template parameter that can be customized.

Finally the factory is defined as show below:

template <typename T>
class GenericFactory_c: boost::noncopyable
{
    public:
        typedef typename T::ObjectType_t ObjectType_t;

        static GenericFactory_c &GetInstance()
        {
            static GenericFactory_c<T> clInstance_gl;

            return clInstance_gl;
        }

        ObjectType_t Create(const String_c &className, const String_c &name) const
        {
            return this->GetObjectCreator(className).Create(name);              
        }               

    protected:
        GenericFactory_c() { }

        friend T;           

        void Register(T &creator)
        {
            setObjectCreators.insert(creator);
        }

        const T &GetObjectCreator(const String_c &className) const
        {
            typename ObjectCreatorSet_t::const_iterator it = setObjectCreators.find(className, ObjectCreatorComp_s<T>());
            if(it == setObjectCreators.end())
                PH_RAISE(OBJECT_NOT_FOUND_EXCEPTION, "[EntityFactory_c::Create]", className);

            return *it;
        }

    protected:          
        typedef boost::intrusive::set<T, boost::intrusive::constant_time_size<false> > ObjectCreatorSet_t;
        ObjectCreatorSet_t setObjectCreators;
};

Also, there is a specialization of the factory for the object with an extra parameter:

template <typename T, typename Y>
class GenericFactory1_c: public GenericFactory_c<T>
{   
    public:
        static GenericFactory1_c &GetInstance()
        {
            static GenericFactory1_c clInstance_gl;

            return clInstance_gl;
        }

        ObjectType_t Create(const String_c &className, const String_c &name, Y param) const
        {
            return this->GetObjectCreator(className).Create(name, param);
        }
};

By the last, I have created this "comparable" object for searching the set of creators:

    template<typename T>
    struct ObjectCreatorComp_s
    {
        bool operator()(const String_c &name, const T &res) const
        {
            return name.compare(res.GetName()) < 0;
        }

        bool operator()(const T &res, const String_c &name) const
        {
            return res.GetName().compare(name) < 0;
        }
    };

To help understand the code above, below I show how it is being used. For example, for defining a new type to be used with the factory, we use:

typedef GenericFactory<ObjectCreator_c<MyObject*> > MyObjectFactory_t;

Define a object creator for a concrete type:

static ObjectCreator<MyObject*> MyConcreteObject_CreatorObject("MyConcreteObject", MyConcreateObject::Create);

Where we assume that "MyConcreteObject" has a static method called "Create" that returns a new instance of the type.

Note also that the creator instance will auto-register itself on the appropriate factory.

For creating an object instance, we could use:

MyObject *obj = MyObjectFactory_t::GetInstance().Create("MyConcreteObject", "objectName");

Any thoughts or suggestion about the design or simple ways to do it are welcome!

Thank you

\$\endgroup\$
  • \$\begingroup\$ Are all your objects default-constructible? \$\endgroup\$ – Kerrek SB Jul 3 '11 at 22:26
  • \$\begingroup\$ No, they all need at least one parameter on the constructor. \$\endgroup\$ – bcsanches Jul 3 '11 at 22:33
  • \$\begingroup\$ I admittedly cannot claim to understand much of this design pattern, but I'm just thinking, if I want to register a class Foo whose only constructor takes two mandatory arguments, Foo(char, double), how would I Create an instance of this class? \$\endgroup\$ – Kerrek SB Jul 3 '11 at 22:53
  • \$\begingroup\$ The design assume that each object type has a static Create member function defined in its class. For registering it, you must create a static creator object, like the code sample: static ObjectCreator<MyObject*> MyConcreteObject_CreatorObject("MyConcreteObject", MyConcreateObject::Create); \$\endgroup\$ – bcsanches Jul 4 '11 at 12:40
  • 3
    \$\begingroup\$ And does the object's Create method take an arbitrary number of arguments, or just one or none? Hmm.. to be honest, I can't really see through this vast amount of indirection and tell whether it makes sense. If you're set on this design, I can't say anything meaningful. If you're willing to consider alternatives, though, perhaps we could start higher up and think it over again. \$\endgroup\$ – Kerrek SB Jul 4 '11 at 12:48
3
\$\begingroup\$

Here are two things that you could do to generalise the Factory pattern and decouple the parameter problem. Either or both would make your life easier.

  1. You could consider using the concept of boost::any to allow an arbitrary number arguments of any type in your constructor list.
  2. Write in the concept of the Null type (an empty class) to represent the case where the parametrised type Y is empty.
  3. Traits classes allow object hierarchies to do behaviour selection based on type.

Your Generic Factory is a creation behaviour that depends on type. A traits class for each of your creatable object types that is able to allow both the construction and interpretation of the array of any type that the factory interface understands will allow you to decouple the Factory from the Type and the Type should always have both a default constructor and possibly custom constructor(s) that the traits class knows how to use.

PS: I am not a fan of this class type naming style ObjectType_t. The type name is a double tautology.

\$\endgroup\$
  • 1
    \$\begingroup\$ +1 for the hatred of hungarian notation... You can buy these things called "IDEs" now that will automatically tell you what type a variable is. \$\endgroup\$ – Ant Sep 10 '11 at 7:17
  • \$\begingroup\$ yes, IDE can tell the type for you, but you have to keep selecting or putting the cursor in everything that you want to know the type. Your eyes are faster than your hands :). \$\endgroup\$ – bcsanches Sep 10 '11 at 16:32
  • \$\begingroup\$ I liked the idea of boost::any, I will try it when I have time. Thanks \$\endgroup\$ – bcsanches Sep 10 '11 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.