views:

57

answers:

1

I'm working on implementing a Factory class along the lines of what is proposed in this response to a previous question:

http://stackoverflow.com/questions/410823/factory-method-implementation-c/534396#534396

It's a Factory that stores a map from strings to object creation functions so I can request different types of objects from the factory by a string identifier. All the classes this factory produces will inherit from an abstract class (Connection) providing a common interface for connections over different protocols (HTTPConnection, FTPConnection, etc...)

I have a good grasp of how the method linked to above works and have got that working.

Where I'm having problems is trying to figure out a mechanism to prevent instantiation of the Connection objects without using the Factory. In order for the Factory to do it's work, I need to provide it an object creation function to store in it's map. I can't provide it the constructor because you can't make function pointers to constructors. So, as in the link above, there has to be a seperate object creation function to return new objects. But to do this, I need to make this creation function either a static method of the class, which the client code would be able to access, or a seperate function which would require either a)that the constructor of the Connection classes be public, or b) make the constructor private and make a non class member creation function be a friend, which isn't inherited and can't be enforced by the abstract base class.

Similarly, if I just made the Factory class friends with the Connection classes it was supposed to produce so it could access their private constructors, that would work, but I couldn't enforce through the abstact base class because friends aren't inherited. Each subclass would have to explicitly be friends with the Factory.

Can anyone suggest a method of implementing what I've described above?

To reiterate the requirements:

1 - Factory that produces a variety of objects all derived from the same base class based on passed in identifier to the Factory's Create method.

2 - All the subclasses that the factory will need to produce will automatically register a creation function and identifier with the factory (see linked SO answer above)

3 - All the subclasses that the factory will produce should not be instantiable (instantiatable?) without going through the Factory

4 - Enforce #3 explicitly as part of the abstract base class using inheritance. Remove the possibility for someone to subclass from the abstract base class while also providing mechanisms to freely instantiate objects.

The overall goal of what I'm trying to achieve is to allow new Connection types to be added to the hierarchy without having to change the Factory class in any way, while also forcing all the subclasses of Connection to not be instantiable directly by client code.

I'm open to the possibility that this is not the best way to achieve what I want, and suggestions of other alternatives are welcome.

EDIT - Will add some code snippets to this when I get home to hopefully make this clearer.

+1  A: 

If I understand you correctly I think you can put some of what you want in the METADECL macro I mention in my answer you link to, ie define a static creator function that is a friend or declare it as a static method. This will make it possible for you to restrict the constructor from public use etc.

Below I try to point out where the METADECL (and METAIMPL) should be. I leave it for you to implement what you need there (I believe in you)

Header file

class MySubClass : public FactoryObjectsRoot {
    METADECL(MySubClass) // Declare necessary factory construct
    :
    :
};

Source file

METAIMPL(MySubClass) // Implement and bootstrap factory construct 
epatel
So for all the subclasses that my Factory will produce, I make the constructor private (to restrict construction of the objects). Then from there I add either a non-member, friend function that creates new objects, or a private static member function in each subclass (and make each subclass friends with the Factory so it can call the private static function)? I'm with you this far. What I'm struggling with though is a way to somehow enforce this for all subclasses without requiring each subclass to explicitly define the friend relationship. Ideally, I'd want to inherit the bases friend-ness
MTLPhil
(continued) which isn't possible in C++ as I understand. Nor is a pure virtual private constructor in the base class which would enforce that all subclasses also have private constructors.
MTLPhil
For example, in the answer in the linked question above couldn't the client code simply call the _name *create_ ## _name() function itself and circumvent the Factory altogether?
MTLPhil
Well, that is what I think can be put in the `METADECL` macro, the friendship and the private constructor, and maybe also some wiring that makes the macros necessary so it's too hard to build it by hand. Maybe I'm not understand you fully. But, know that any developer can write `#define private public` before any `#include` and then can to bypass it all...
epatel
The `*create ## _name()` function should actually be declared as static so it's only accessible locally.
epatel
Ok, I see. So I provide the macro and anyone subclassing to add another Factory product simply uses the macro I've already made. Maybe I am putting too much emphasis on this ideal of it being self contained and having the creation methods of the product classes being hidden from client access. Better to get it working first then worry about my quirky ideas of making it 'elegant'. Really appreciate your input. Thank you.
MTLPhil
Ahh, that's what I was missing! I was forgetting that a non-member static function has internal linkage only. So it won't be visible to the client code, but the self-registering with the Factory will still allow the Factory to 'see' it and call it.
MTLPhil
Yes, I maybe explained it badly. The macros should be used for all factory product classes. The macro system is quirky but it has the stringify feature which makes it nice to minimize typing...
epatel