How to implement a generic Factory that supports pattern covariance?

How to implement a generic Factory in C ++ 14 that supports pattern 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 the DerivedClass constructor. It can be assumed that the BaseClass constructor and DerivedClass identical.


To avoid the XY problem, the reason I need it is because I want to use dependency injection ( boost :: di ) to achieve maximum verifiability.

For example, if there is a class A that creates Socket instances, I want it to depend on the Factory<ISocket> . In real code, I would enter Factory<Socket> , and in test code I would enter Factory<Mock<ISocket>> , so I can test class A 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>(); } }; 

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 use is suitable for me, and it works.

The next thing I need to add is support for constructor arguments. I tried adding a variational pattern to create :

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

... but it looks like you have no virtual methods with templates.


  • Am I going in the right direction?
  • If so, how do I add support for constructor arguments in my implementation?

Thanks!

+7
c ++ dependency-injection unit-testing c ++ 14 factory
source share
2 answers
+1
source share

OK, I found one solution, but it is not very pretty:

 template <typename T, typename ...TArgs> struct BaseFactory { virtual std::unique_ptr<T> create(TArgs&&... args) = 0; }; template <typename TInterface, typename TImplementation, typename ...TArgs> struct Factory : public BaseFactory<TInterface, TArgs...> { virtual std::unique_ptr<TInterface> create(TArgs&&... args) override { return std::make_unique<TImplementation>(std::forward<TArgs>(args)...); } }; using ISocketFactory = BaseFactory<ISocket, int>; using SocketFactory = Factory<ISocket, Socket, int>; int main() { std::shared_ptr<ISocketFactory> socket_factory = std::make_shared<SocketFactory>(); std::unique_ptr<ISocket> socket = socket_factory->create(1234); socket->read(); socket->write(); } 

The idea is to pass the constructor arguments of the implementation class in BaseFactory and Factory patterns. In this case, the Socket constructor should look something like this:

 Socket(int n); 

Do you have any ideas how to optimize this? (less boilerplate code)

+1
source share

All Articles