Yep, I see your problem. You still need need to know your type at
runtime in order to instantiate it. You need to separate your type
information from your creation.
Yes.
}
base* create(std::string id) {
return factory_[id]();
}
What about totally unrelated types. Types who do not derive from base?
Yes, I noticed Alexandrescu's solution addresses this with scattered
hierarchies. Yours don't. Derived's type is derived from base, which is
exactly what MakeX would give you in:
struct X{...};
struct XD : X{...};
struct Factory
{
X* makeX() const;
}
struct DFactory: Factory
{
XD* makeX() const;
}
Here XD represents your derived. I see no added advantange when
weighing your example against the original. The other thing you did not
address is constructors with many arguments. I know prototyping is one
way to address this. Unfortunately it is not good enough as not all
aggregates of the same type requires the same prototype.
int main() {
Factory factory;
factory.Register(std::string("base"), &createBase);
factory.Register(std::string("derived"), &createBase);
base* b = factory.create(std::string("base"));
return 0;
}
Less than ideal example...
How about:
Client::Client( const Factory& f)
{
//I'm still exposed to the interface of the entire factory... no
gains!
//What if I were required to make a type not derived from base?
}
What I'm getting at, is that each factories interface must be
ultra-concise (narrow). It should only represent one type. Andrei does
achieve this, yes - by means of the leaves of the scattered hierarchy.
The Clients can now state:
Client::Client( const AbstractFactoryUnit<MyServer>& f)
{
//Now I'm only exposed to the interface of the generic factory that
never changes,
// as well as that of MyServer, which is ok as I'll be using it. Of
course, if I'm only passing
// it along this is not necessary (Forward declare is sufficient).
}
This does not entirely solve the many arguments to a constructor
problem. I have scanned chapter 9 in Modern CPP briefly, but did not
see this being addressed (but I may have been to brief). I read the
part about using prototype. Yes, one only has one prototype per type,
and that prototype may not be the one that the client is requiring.
Sometimes the client may have data that may influence the prototype
(i.e. the prototype is constructed differently due to requirements by
client), right? This is rare though, and the fact that the interface of
the factory is narrowed down to one type helps allot. Agreed.
Now, my point is - making the client dependent on a non-abstract class
(and using an abstract pimpl as bridge), may solve some problems not
solved by Factories and Prototypes (like providing arguments to
constructors). Other solutions are what I call <delayed construction>
idiom. This is similar, actually, to the solution in MCPP design.
Simply:
template <class T>
struct CreationAbstractor
{
virtual T* create() const = 0;
}
Client::Client( const CreationAbstractor<MyType>& myTypeCreator )
: myType( myTypeCreator.create() )
{
}
Now the Client can dicate quite easily what type he requires. Whoever's
responsible for instantiating Client, provides the necessary
construction parameters:
struct MyTypeAbstractor : public CreationAbstractor<MyType>
{
MyTypeAbstractor( int x, int y, int z);
virtual MyType* create() const{ return new DerivedFromMyType( x,y,z
); }
private:
int x_;
int y_;
int z_;
};
This does assume that the one creating client, has enough info to
provide the construction parameters. Note that the one creating client
does not need to be exposed to the interface of MyType, though.
Kind regards,
Werner