Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make my Factory's Header not dependent on the templated objects it creates?

I have an abstract base class like so:

class AbstractBaseClass
{}; 

a templated concrete class that derives from it:

template<class T>
class ConcreteClass : public AbstractBaseClass
{
public:
   ConcreteClass(T input) : data(input) {}
private:
    T data;
};

AndI have a factory class that creates AbstractBaseClasses

class MyFactory
{
public:
   boost::shared_ptr<AbstractBaseClass> CreateBlah();
   boost::shared_ptr<AbstractBaseClass> CreateFoo();

   template<class T>
   boost::shared_ptr<AbstractBaseClass> Create(T input)
   {
      return boost::shared_ptr<AbstractBaseClass>(new ConcreteClass<T>(input));
   }
};

The problem with this, is that now EVERYTHING that uses MyFactory has to include the entire implementation to ConcreteClass. Ideally, I want nothing but MyFactory to know about ConcreteClass.

Is there any way to architect this to achieve this goal? (Besides manually making a new Create function in MyFactory for every type I want instead of templating it).

like image 796
Lima Beans Avatar asked Sep 14 '10 04:09

Lima Beans


1 Answers

you'll need to put the factory implementation into the implementation file (which you mentioned you'd like to avoid, but it is the lesser evil unless the interfaces are small, and/or your projects are small).

of course, there are a few other ways you could approach this, such as putting the implementation into base classes, and making derived bases factories, or using some other really weird template syntax to reduce instantiations in dependent translations. this really comes down to convenience and scale for your project. if you are working on one or more large projects, then full abstraction wrt instantiation will serve your needs best in the long run (assuming you need dynamic polymorphism and memory).

you may also try other approaches (such as overloading) to reduce errors by using type-safety.

the short answer is that you'll really need to abstract the interfaces/instantiation into one or multiple implementation files to remove header dependencies - very common idiom, and many ways to tackle it. you can over course further divide and use polymorphism for your factories as well.

you may also use template forward declarations to minimize the sets to the compilation unit. provided:

/** in MyIntermediateFactory.hpp */
class MyIntermediateFactory {
public:
    static template<class T> boost::shared_ptr<T> Create(float);
};

/** in Concrete.hpp */
template<Concrete>
boost::shared_ptr<T> MyIntermediateFactory::Create<Concrete>(float arg) {
    /* … */
}

using this you can select portions of programs/interfaces which you need in the library, then wrap it all up in a real Factory (for the build at hand). the linker/instantiation should fail along the way if you actually attempt to request a creation which is not visible.

there a lot of options, really - you need to figure out how big your scale is in order to determine what to abstract (or not). instantiation requires interface, to remove header dependencies, you'll have to abstract the instantiation someplace.

like image 65
justin Avatar answered Oct 12 '22 22:10

justin