The solution proposed by @Lilshieste has the same flaw of your original implementation, you have to manually maintain a "switch-heavy" statement, even worst, in @Lilshieste solution you have increase the number of factories given as parameter to the RequestFactory.
Since you don't mentioned any performance issue I'll propose a little slower, but more solid solution.
I do not agree that this need aggregation, the reason is that in your case you just don't have many dependencies, you need polymorphic behaviour, wich does not require aggregation but construction of an appropiate interface to deal with.
Observation 1:
Since you are using C++11, use the new enums instead of "int" for the ID (that will give usefull compile time errors).
Observation 2:
Reverse the problem. Dont let the generic RequestFactory depends on Concrete Factories, instead let ConcreteFactories register themselves in the RequestFactory!
class RequestOneFactory: public virtual AbstractRequestFactory{
//note: no member variables!
public:
RequestOneFactory( std::shared_ptr<RequestFactorySupplier> factory){
factory->register( getptr(),ID);
}
std::shared_ptr< Request> create() const{
std::shared_ptr< Request> request =
std::make_shared< RequestOne>( /* Dependencies*/);
return request;
}
};
Every Factory have the same Interface, since you are doing DI, you probably want the factory to be managed. Just inherit from enable_shared_from_this.
class AbstractRequestFactory: std::enable_shared_from_this< AbstractRequestFactory>{
public:
virtual ~AbstractRequestFactory(){}
virtual std::shared_ptr< Request> create() const = 0;
std::shared_ptr<AbstractRequestFactory> getptr() {
return shared_from_this();
}
};
The RequestFactory has now no constructor dependencies, also note that I broke up its interface in 2 parts so that different users of the factory can do different things.
#include <unordered_map>
#include <RequestFactorySupplier.hpp>
#include <RequestFactoryUser.hpp>
#include <AbstractRequestFactory.hpp>
class RequestFactory: public virtual RequestFactorySupplier
,public virtual RequestFactoryUser{
//associative container.
std::unordered_map< RequestID, std::shared_ptr< AbstractRequestFactory>> map;
public:
RequestFactory()=default;
~RequestFactory()=default;
//IMPLEMENTS: RequestFactorySupplier
virtual void register( std::shared_ptr< AbstractRequestFactory> fac, RequestID id){
if(map.find(id)!=map.end()){
//ID already used.. throw error, assert(false), what you want.
}
map[id] = fac;
}
//IMPLEMENTS: RequestFactoryUser
virtual std::shared_ptr< Request> create(RequestID id){
if(map.find(id)==map.end())
throwSomething(); //No factory for such ID
return map[id]->create();
}
};
If you are using a Dependency Injection framework
now the work to wire up everything is very simple, the following example uses Infectorpp (wich I wrote), you can do similiar things with other frameworks of course.
#include <Infectorpp/InfectorContainer.hpp>
int main(){
Infector::Container ioc;
//register generic factory with 2 interfaces
ioc.bindSingleAs<RequestFactory, RequestFactorySupplier,RequestFactoryUser>();
//for each concrete factory register it
ioc.bindSingleAsNothing<RequestOneFactory>();
ioc.bindSingleAsNothing<RequestTwoFactory>();
//...
//wire the generic factory
ioc.wire<RequestFactory>();
//wire the factories (dependencies injected here)
ioc.wire<RequestOneFactory, RequestFactorySupplier>();
ioc.wire<RequestTwoFactory, RequestFactorySupplier>();
//...
//build the factories (let them register in RequestFactorySupplier)
{
ioc.buildSingle<RequestOneFactory>();
ioc.buildSingle<RequestTwoFactory>();
//you will not have references to them but RequestFactory will: Good!
}
//Your classes can use RequestFactoryUser to create RequestObjects.
//application run!
return 0;
}
Also consider creating objects wrapped around a std::unique_ptr for those factories instead of a std::shared_ptr (if unique_ptr is enough never use std::shared_ptr).