c++unit-testingdependency-injectionc++14factory

How to implement a generic Factory that supports template covariance?


I want to achieve something like this:

std::shared_ptr<Factory<BaseClass>> factory = 
    std::make_shared<Factory<DerivedClass>>();

auto x = factory->create(arg1, arg2, arg3);

Note that in factory->create, you can pass any arguments to DerivedClass constructor. It is okay to assume that the BaseClass constructor and the DerivedClass are identical.


To avoid the XY Problem, the reason I need this is because I want to use dependency injection (boost::di) to achieve maximum testability.

For example, if there's a class A that creates Socket instances, I want it to depend on a Factory<ISocket> service. In the real code, I'd inject Factory<Socket>, and in the testing code, I'd inject Factory<Mock<ISocket>>, so I can test the A class without actually creating a real socket.


This is my current attempt:

template <typename T>
struct BaseFactory {
    virtual std::unique_ptr<T> create() = 0;
};

template <typename TInterface, typename TImplementation>
struct Factory : public BaseFactory<TInterface> {
    virtual std::unique_ptr<TInterface> create() override {
        return std::make_unique<TImplementation>();  
    }
};

The current usage is something like:

std::shared_ptr<BaseFactory<ISocket>> factory = 
    std::make_shared<Factory<ISocket, Socket>>();

auto x = factory->create();

Although not ideal (you need to specify the base class in Factory), this usage is fine for me and it works.

The next thing I need to add is support for constructor arguments. I've tried to add variadic template to create:

template <typename ...TArgs>
virtual std::unique_ptr<T> create() = 0;

... but it looks like you can't have virtual methods with templates.

Am I going in the right direction? If yes, how should I add support for constructor arguments in my implementation?


Solution

  • Here is the solution for boost::di:

    http://boost-experimental.github.io/di/extensions/index.html#factory