Actually I lied - we only have a couple classes right now that are
subclassed per customer, but we are continuing to increase the amount
of OO in the design (it was not great to start with) and will be
creating quite a number of new classes to be subclassed. The reason I
mention this is that I find the current implementation for the
following problem acceptable for the current number of cases, but
probably not for an increased number.
My question involves the "virtual constructor" or "object factory"
idea. The places where these custom classes must be instantiated is in
generic code, so we can't know the specific type. I've solved this so
far by creating a single ObjectFactory class with class member
functions to return a specific type. The program is written in C++ and
I think we need to talk specifically in terms of C++ in this case (I
felt I'd get better help in this forum than a C++ one.) The
ObjectFactory is in a cpp file with the same name for all our customer
projects, but the code is different in each - all the static function
names are the same, but the specific type returned is different in each
versin of the file.
I wonder if rather than continuing to adding a long list of functions
to this class, it would be best to subclass the ObjectFactory so that
each customer has it's own type of one. Then create one dynamically so
that the function calls can be made through a generic pointer.
This of course begs the question of how to create the correct
ObjectFactory type for each customer project. I've read the Design
Patterns Factory Method pattern, and it seems OK until I get to the
last part I just mentioned. How exactly would I a) create the correct
type of ObjectFactory, or b) create a generic ObjectFactory and send it
the correct parameter to identify each unique customer project?
Remember this must occur from a generic point in the code. Would
something like a preprocessor variable, or global constant that is
somehow specified differently for each customer, be a total hack?
Thanks,
--
Regards,
Jeff
Sent via Deja.com http://www.deja.com/
Before you buy.
I'm having a very similar problem. Look in comp.lang.c++.moderated at the
two treads "Relying on RTTI" and "Polymorphism across processes and
networks" - some clever people have replied, and the discussion is still
going.
At www.ootips.org, there're some good articles, especially
http://www.ootips.org/late-creation.html, which pertains to what we're both
doing. I haven't read the GoF book (yet!), so I can't comment on how this
relates to the patterns described there.
Anyway, here's what I'm planning to do (more or less):
Each class in the hierarchy that this pertains to has a nested class - a
factory. This class contains just one virtual function, which creates a
class of the desired type (possibly from some stream or file or loader
abstraction - see the posts in c.l.c++.m) on the heap and return a pointer
to it as a pointer to the hierarchy's base class. Each class then contains a
static member of the factory class. When the factory class member is
created, it's constructory will register itself (using the "this" pointer)
with a map.
This map is stored somewhere accessible (probably a static member of a
"super-factory", which will use the loader/stream/file abstraction to
determine which class to construct - see later), and maps an object name (or
id) to a pointer to that object's factory. To make this possible, the
factories (nested clases) should be derived frome some common base class -
such as a nested class in the abstract base for the class hierarchy you're
working with. To get these names, I'll use a traits template, much like
char_trats<> of the standard library. The idea is that each class in the
hierarchy is accompanied by a specialization of a "traits<>" template, such
as:
template <class T>
class traits
{
static const char *name () const { return ""; }
};
So, class Foo has in its header file:
template <>
class traits<Foo>
{
static const char *name () const { return "Foo's identifier"; }
};
Which means that Foo and its derivatives (if they have no particular
specialization, that is) can be identifed by:
traits<Foo>::name ()
Note that I'm a little unsure about the syntax of that right here right
now - I'll need to look up whether the "class traits<Foo>" part is correct -
maybe it should be "class traits<Foo&>" instead to allow polymorphism, or
maybe I got something else wrong. If you've got "The C++ Programming
Language", look in there, and look at char_traits<> in your STL
implementation.
Anyway, say the map is called type_map and that the basic factory class has
a virtual function
Base *create () const = 0;
You can get a factory for foo by using:
Base *b = type_map[traits<Foo>::name ()]->create ();
Similarly, you can store Foo's name in a file or send it across a network
and have the reader of the file or client side of the network use the
type_map table, too.
Hope this at least gets you started. Have a look at www.objectmentor.com for
some other good OO ideas that I've found useful in relation to this project
(and this problem) - especially the articles by Robert C. Martin.
Martin
> Our project (a set of DLLs really, but let's just say a program) has a
> common base of function, and a number of classes that we have
> customized on a per customer basis. We have customized it for a dozen
> or so customers, and expect at least twice that many eventually.
> Consequently, we have a quite a number of subclasses that represent
> specializations of classes for specific customer differences.
> I wonder if rather than continuing to adding a long list of functions
> to this class, it would be best to subclass the ObjectFactory so that
> each customer has it's own type of one. Then create one dynamically so
> that the function calls can be made through a generic pointer.
Since your project is Windows-only (it uses DLLs), why don't you just
use COM for this? You can store the CLSIDs of the clases that have to be
instantiated for each customer in a configuration file/database, this
allows you to have a single ObjectFactory that works for all customers.
Make the COM-classes all implement a specific interface
(IConstructedObject) that allows you to pass initialisation parameters
to the objects, because in COM you do not have the prossibility to pass
parameters to the constructor.
Christof