Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to design a simple C++ object factory?

Tags:

c++

factory

In my application, there are 10-20 classes that are instantiated once[*]. Here's an example:

class SomeOtherManager;

class SomeManagerClass {
public:
    SomeManagerClass(SomeOtherManager*);
    virtual void someMethod1();
    virtual void someMethod2();
};

Instances of the classes are contained in one object:

class TheManager {
public:
    virtual SomeManagerClass* someManagerClass() const;
    virtual SomeOtherManager* someOtherManager() const;
    /** More objects... up to 10-20 */
};

Currently TheManager uses the new operator in order to create objects.

My intention is to be able to replace, using plugins, the SomeManagerClass (or any other class) implementation with another one. In order to replace the implementation, 2 steps are needed:

  1. Define a class DerivedSomeManagerClass, which inherits SomeManagerClass [plugin]
  2. Create the new class (DerivedSomeManagerClass) instead of the default (SomeManagerClass) [application]

I guess I need some kind of object factory, but it should be fairly simple since there's always only one type to create (the default implementation or the user implementation).

Any idea about how to design a simple factory like I just described? Consider the fact that there might be more classes in the future, so it should be easy to extend.

[*] I don't care if it happens more than once.

Edit: Please note that there are more than two objects that are contained in TheManager.

like image 272
kshahar Avatar asked Dec 02 '08 09:12

kshahar


2 Answers

Assuming a class (plugin1) which inherits from SomeManagerClass, you need a class hierarchy to build your types:

class factory
{
public:
    virtual SomeManagerClass* create() = 0;
};

class plugin1_factory : public factory
{
public:
    SomeManagerClass* create() { return new plugin1(); }
};

Then you can assign those factories to a std::map, where they are bound to strings

std::map<string, factory*>  factory_map;
...
factory_map["plugin1"] = new plugin1_factory();

Finally your TheManager just needs to know the name of the plugin (as string) and can return an object of type SomeManagerClass with just one line of code:

SomeManagerClass* obj = factory_map[plugin_name]->create();

EDIT: If you don't like to have one plugin factory class for each plugin, you could modify the previous pattern with this:

template <class plugin_type>
class plugin_factory : public factory
{
public:
   SomeManagerClass* create() { return new plugin_type(); }
};

factory_map["plugin1"] = new plugin_factory<plugin1>();

I think this is a much better solution. Moreover the 'plugin_factory' class could add itself to the 'factory_map' if you pass costructor the string.

like image 177
Emiliano Avatar answered Oct 18 '22 16:10

Emiliano


I think there are two separate problems here.

One problem is: how does TheManager name the class that it has to create? It must keep some kind of pointer to "a way to create the class". Possible solutions are:

  • keeping a separate pointer for each kind of class, with a way to set it, but you already said that you don't like this as it violates the DRY principle
  • keeping some sort of table where the key is an enum or a string; in this case the setter is a single function with parameters (of course if the key is an enum you can use a vector instead of a map)

The other problem is: what is this "way to create a class"? Unfortunately we can't store pointers to constructors directly, but we can:

  • create, as others have pointed out, a factory for each class
  • just add a static "create" function for each class; if they keep a consistent signature, you can just use their pointers to functions

Templates can help in avoiding unnecessary code duplication in both cases.

like image 20
UncleZeiv Avatar answered Oct 18 '22 15:10

UncleZeiv