Closing a factory method using dynamic object creation.

4

4Space

I've hit something of a snag.

Problem:

In a DSP application we have data profiles with a varying number of points.
We have a number of Discrete Fourier transforms that are optimised for a
specific number of points. Currently, any client of these DFT classes must
switch on the number of points in the profile and instanciate the correct
DFT algorithm.

switch( size )
case 2000:
return new DFT2000Point();
case 3600:
return new DFT3600Point();
// etc...

Clearly this is horse poo.

I've just created a factory class that takes a profile, and returns a base
class pointer to the correct concrete type of DFT. It does this currently by
again, switching on the number of points in the profile. Still not good, but
at least the clients are not having to replicate this code. The thing that
bothers me is that this method is not closed to change. Every time we add a
new DFT class, we have to modify this switch. Clearly I'd like to avoid
this.

So, how do I do it?

My first feeling is that their is an obvious mapping between the concrete
class, and the number of points. So I should perhaps have a map:

class DFTFactory
{
public:

Map<int size, const type_info &theType>;

// .....


With a create method:

auto_ptr<DFTBase> Create( int numberOfPoints )
{
//....


So my question is... how do I create an instance of a concrete class from
its type_info?


Cheers,

4Space
 
K

Karl Heinz Buchegger

I would do it this way:

You rmap is a good idea, so keep it, but instead of storing type_info's,
why not store pointers to already existing objects?

class DFTFactory
{
public:

void Register( int Size, DFTBase* pDFT );
DFTBase* LookUp( int Size );

private:
std::map< int, DFTBase* > m_TemplateObjects;
};

Now you derive all your DFT-Engines from a common DFTBase.
Every DFT-Engine creates at startup one instance and registers
that instance with the DFTFactory.
When the time has come to use a transformation object, the Factory
can look up one of those objects based on the requested size.
It then can either return a pointer to that object, or use a virtual
constructor idiom to create a copy from it, by calling the virtual
function Clone():

class DFTBase
{
public:
....
virtual DFTBase* Clone() = 0;
....
};

class DFT2000Point : public DFTBase
{
public:
....
virtual DFTBase* Clone() { return new DFTBase2000Point( *this ); };
....
};

When thinking of it, I believe that having only one DFT object around
(for each point size) should be sufficient, so you additionally might
investigate in exploring the Singleton pattern. But this is something
only you can decide.
 
4

4Space

class DFTFactory
{
public:

void Register( int Size, DFTBase* pDFT );
DFTBase* LookUp( int Size );

private:
std::map< int, DFTBase* > m_TemplateObjects;
};

Now you derive all your DFT-Engines from a common DFTBase.
Every DFT-Engine creates at startup one instance and registers
that instance with the DFTFactory.

How should I tackle the registration? Use static initialisation in the
concrete class to instanciate itself and then register itself?

Cheers,

4Space
 
K

Karl Heinz Buchegger

4Space said:
How should I tackle the registration? Use static initialisation in the
concrete class to instanciate itself and then register itself?

Cheers,

eg. a simple solution

int main()
{
DFT2000Points The2000PointDFT;
DFT2000Points The4000PointDFT;
DFTFactory TheDFTFactory;

TheDFTFactory.Register( 2000, &The2000PointDFT );
TheDFTFactory.Register( 4000, &The4000PointDFT );

...
}

or as you say: create a static instance and let it register
in the ctor

or ...

There are a number of possibilities
 
4

4Space

eg. a simple solution
int main()
{
DFT2000Points The2000PointDFT;
DFT2000Points The4000PointDFT;
DFTFactory TheDFTFactory;

TheDFTFactory.Register( 2000, &The2000PointDFT );
TheDFTFactory.Register( 4000, &The4000PointDFT );

...
}

This just shuffles the problem to Main(), as it must be updated every time
we add a new concrete DFT.
or as you say: create a static instance and let it register
in the ctor

This sounds the way. I hadn't considered a combination of factory and
prototype.
or ...

There are a number of possibilities

Will investigate these also :¬) Many thanks for your input.

Cheers,

4Space
 
K

Karl Heinz Buchegger

4Space said:
How should I tackle the registration? Use static initialisation in the
concrete class to instanciate itself and then register itself?

A different concept which uses a creation function could look like this.
It has the advantage that each object is created only when needed. If your
DFT classes contain a substantial amount of members, then this would
be better.


#include <iostream>
#include <map>
using namespace std;

class DFTBase
{
public:
virtual void Use() { cout << "Error: can't use base class DFTBase\n"; }
};

/////////////////////////////////////////////////////////////
//
typedef DFTBase* (*CreateFnct)();

class DFTFactory
{
public:
void Register( int Size, CreateFnct pFunct )
{
m_Map[Size] = pFunct;
}

DFTBase* LookUp( int Size )
{
CreateFnct pFunc = m_Map[Size];
if( pFunc )
return pFunc();
return NULL;
}

protected:
std::map< int, CreateFnct > m_Map;
};

/////////////////////////////////////////////////////////////
//
class DFT2000 : public DFTBase
{
public:
static DFTBase* Create()
{
if( !m_p2000DFT ) {
cout << "2000 created\n";
m_p2000DFT = new DFT2000;
}
else
cout << "using existing 2000 engine\n";

return m_p2000DFT;
}

static void RegisterWith( DFTFactory& Factory )
{
Factory.Register( 2000, Create );
}

virtual void Use() { cout << "This is DFT for 2000 points\n"; }

private:
DFT2000() {}

static DFT2000* m_p2000DFT;
};

DFT2000* DFT2000::m_p2000DFT = 0;

/////////////////////////////////////////////////////////////
//
class DFT4000 : public DFTBase
{
public:
static DFTBase* Create()
{
if( !m_p4000DFT ) {
cout << "4000 created\n";
m_p4000DFT = new DFT4000;
}
else
cout << "using existing 4000 engine\n";

return m_p4000DFT;
}

static void RegisterWith( DFTFactory& Factory )
{
Factory.Register( 4000, Create );
}

virtual void Use() { cout << "This is DFT for 2000 points\n"; }

private:
DFT4000() {}

static DFT4000* m_p4000DFT;
};

DFT4000* DFT4000::m_p4000DFT = 0;

/////////////////////////////////////////////////////////////
//
int main()
{
DFTFactory Factory;

DFT2000::RegisterWith( Factory );
DFT4000::RegisterWith( Factory );

Factory.LookUp( 2000 )->Use();
Factory.LookUp( 4000 )->Use();
Factory.LookUp( 2000 )->Use();

return 0;
}


Cheers,

4Space

--
Karl Heinz Buchegger, GASCAD GmbH
Teichstrasse 2
A-4595 Waldneukirchen
Tel ++43/7258/7545-0 Fax ++43/7258/7545-99
email: (e-mail address removed) Web: www.gascad.com

Fuer sehr grosse Werte von 2 gilt: 2 + 2 = 5
 
H

H. S. Lahman

Responding to 4Space...
I've hit something of a snag.

Problem:

In a DSP application we have data profiles with a varying number of points.
We have a number of Discrete Fourier transforms that are optimised for a
specific number of points. Currently, any client of these DFT classes must
switch on the number of points in the profile and instanciate the correct
DFT algorithm.

switch( size )
case 2000:
return new DFT2000Point();
case 3600:
return new DFT3600Point();
// etc...

Clearly this is horse poo.

Not necessarily. There is nothing inherently wrong with parameterizing
behavior with external data. The real issue is the level of abstraction
of the method containing the switch. If the fact that there are
multiple DFT packages is an implementation issue and not relevant to
problem being solved (e.g., one has chosen a third party DFT package
that has multiple entry points), then the dispatch is just part of the
the implementation of the responsibility to apply a DFT. In that case
it would probably be a bad idea to expose it beyond the client method.

OTOH, if the existence of multiple DFT algorithms is important to the
problem solution (e.g., the requirements specify the algorithm to use),
then one needs to expose that decision in the OOA/D. In fact, it is a
good idea to identify the decision as someone's unique, explicit
responsibility. The most straight forward way to do that is through
relationship instantiation:

0..* 1
[Client] ---------------------- [DFT]
A
|
+-------+-------+
| |
[2000DFT] [3600DFT]

Now someone decides which [DFT] subclass is appropriate for a particular
Client and instantiates the relationship to an instance of that
subclass. Then a Client simply addresses a message to whoever is at the
other end of the relationship (e.g., invokes the superclass method,
DFT->doIt() via a DFT* pointer).

Note that this is really just a simplified Strategy pattern. Whether
one needs the full decoupling of Strategy will depend on specific
context. It can be a simple as...
I've just created a factory class that takes a profile, and returns a base
class pointer to the correct concrete type of DFT. It does this currently by
again, switching on the number of points in the profile. Still not good, but
at least the clients are not having to replicate this code. The thing that
bothers me is that this method is not closed to change. Every time we add a
new DFT class, we have to modify this switch. Clearly I'd like to avoid
this.

....initializing a DFT* pointer in [Client]. IME, one would usually
initialize an instance of each DFT subclass at startup, probably by a
Factory. However, the size is likely to be dynamically defined at run
time. Whoever determines the size would need to instantiate the
relationship by setting the DFT reference attribute in the Client in hand.

So the only complication is mapping the size to a DFT instance (i.e.,
finding the right subclass instance to assign to the reference
attribute). How one does that will depend upon the context of when and
how the size is determined. One can have a static Find method in the
DFT class to look up the instance based on size; one can have the
decision maker do its own table lookup from a table that was initialized
as part of its construction; or any of several other variations. Either
way, a Factory will probably play a startup role in initializing a
lookup table somewhere.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
(e-mail address removed)
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindersol.com
(888)-OOA-PATH
 
D

Dave Harris

This sounds the way. I hadn't considered a combination of factory and
prototype.

Then you just have to ensure the file containing the class is linked into
the app. This can go wrong - some linkers will leave out an object file if
nothing it contains is needed by the main program, even if it contains
statics with constructors. Especially if the object file is in a library.
You may end up having to force the file to link in, which leads back to
the original problem :)

-- Dave Harris, Nottingham, UK
 
D

Doc O'Leary

4Space said:
switch( size )
case 2000:
return new DFT2000Point();
case 3600:
return new DFT3600Point();
// etc...

Clearly this is horse poo.

Yes, C++ *is* pretty crappy.
So, how do I do it?

Switch to a better implementation language. A solution in Objective-C
(using the OpenStep API):

sizedDFTClass = NSClassFromString([NSString stringWithFormat:
@"DFT%dPoint", size]);
 
K

Kevin Goodsell

Doc said:
Yes, C++ *is* pretty crappy.

Why do you feel it's necessary to post this in comp.lang.c++? Are you a
troll?
So, how do I do it?


Switch to a better implementation language. A solution in Objective-C
(using the OpenStep API):

sizedDFTClass = NSClassFromString([NSString stringWithFormat:
@"DFT%dPoint", size]);

Objective C is off-topic in comp.lang.c++. Please don't post such things
here.

-Kevin
 
E

Eric Beyeler

4Space said:
How should I tackle the registration? Use static initialisation in the
concrete class to instanciate itself and then register itself?

Make your Register return a bool. Give each derived class a static
Create fn that returns new <DerivedClass>. In the factory store a map
of fn's
Then, in the source file for every derived class, put something like
the following:

namespace
{
bool bRegistered = TheDFTFactory.Register( 2000,
The2000PointDFT::Create );
}

Eric Beyeler
 
D

Doc O'Leary

Kevin Goodsell said:
Why do you feel it's necessary to post this in comp.lang.c++? Are you a
troll?

Welcome to Usenet, Freshman Goodsell. Educate yourself regarding cross
posts.
Objective C is off-topic in comp.lang.c++. Please don't post such things
here.

All OO topics are fair game in comp.object, though. I agree that the
OP's cross post was questionable, what with C++ being such a horrid OO
language with no real object support. Feel free to edit followups
(another thing that you probably need to learn about) if you're so
fragile that you cannot stand to hear about alternatives.
 
K

Kevin Goodsell

Doc said:
Welcome to Usenet, Freshman Goodsell. Educate yourself regarding cross
posts.

Hmmmm...

http://groups.google.com/groups?q=author:Doc author:O'Leary
Results 1 - 10 of about 2,090

http://groups.google.com/groups?q=author:kevin author:goodsell
Results 1 - 10 of about 6,040

This "Freshman" has nearly 3 times as many posts as you. And I'm very
familiar with the cross-post, thank you very much. Please educate
yourself regarding netiquette.

(Granted, this doesn't actually prove anything since you could have
posted under a different name. Personally, I've never felt the need to
hide behind an alias on Usenet.)
All OO topics are fair game in comp.object, though.

Fine, but you also posted to comp.lang.c++.
I agree that the
OP's cross post was questionable,

I only take issue with your cross-posting an intentionally inflammatory
message here, when you could have simply removed comp.lang.c++ from the
crosspost list. Clearly your only reason for not doing so was to pick a
fight with this group.
what with C++ being such a horrid OO
language with no real object support.

And the trolling continues.
Feel free to edit followups
(another thing that you probably need to learn about)

Nope, I've got that down also.
if you're so
fragile that you cannot stand to hear about alternatives.

My interests in programming extend well beyond C++ already. The issue
here is your trolling, not my language preferences.

I've also mastered the killfile, fortunately.

-Kevin
 
D

Doc O'Leary

Kevin Goodsell said:
This "Freshman" has nearly 3 times as many posts as you. And I'm very
familiar with the cross-post, thank you very much. Please educate
yourself regarding netiquette.

Wow. You know, I was actually referring to your maturity level, and
your turning it into a pissing match supports my point in ways I never
imagined. Clearly you think quantity makes up for quality, and clearly
you think your search is representative, yet it shows no posts for you
prior to 1998 while my search goes all the way back to 1991. So maybe,
just maybe, you need to rethink your position as to who has the "junior"
position here. While you're at it, rethink your choice of language,
because C++ is still a terrible OO language.
(Granted, this doesn't actually prove anything since you could have
posted under a different name. Personally, I've never felt the need to
hide behind an alias on Usenet.)

Neither have I. But if you want to go full-blown ego surfing (and you
seem like the type that does), you'll find what I expect is my first
Usenet posting here:

http://groups.google.com/groups?q=author:[email protected]&start=13
0&hl=en&as_drrb=b&as_mind=12&as_minm=5&as_miny=1981&as_maxd=8&as_maxm=4&a
s_maxy=1992&selm=2605%40ux.acs.umn.edu&rnum=131
I only take issue with your cross-posting an intentionally inflammatory
message here, when you could have simply removed comp.lang.c++ from the
crosspost list. Clearly your only reason for not doing so was to pick a
fight with this group.

No, as it turns out I didn't care to read the Newsgroups line. I
thought the OP was asking about proper OO development, not hacking C++.
That is why I indicated that it was an odd sort of cross post to have.
I've also mastered the killfile, fortunately.

Excellent. Then I shall not be contributing to your future "quantity"
scores. You truly *are* the winner, Freshman Goodsell!
 
U

Uncle Bob (Robert C. Martin)

How should I tackle the registration? Use static initialisation in the
concrete class to instanciate itself and then register itself?

Do the registration in 'main'.


Robert C. Martin | "Uncle Bob"
Object Mentor Inc.| unclebob @ objectmentor . com
PO Box 5757 | Tel: (800) 338-6716
565 Lakeview Pkwy | Fax: (847) 573-1658 | www.objectmentor.com
Suite 135 | | www.XProgramming.com
Vernon Hills, IL, | Training and Mentoring | www.junit.org
60061 | OO, XP, Java, C++, Python | http://fitnesse.org
 
U

Uncle Bob (Robert C. Martin)

This just shuffles the problem to Main(), as it must be updated every time
we add a new concrete DFT.

That doesn't bother me all that much. I often make 'main' (and its
cronies) the functions that aren't closed to modification.

If you absolutely *must* close main, then you can create one dll per
algorithm. Put all the necessary dlls in a directory. 'main' can
search the directory, load each dll, and using a standard function
call ask each one to register its algorithm.

Robert C. Martin | "Uncle Bob"
Object Mentor Inc.| unclebob @ objectmentor . com
PO Box 5757 | Tel: (800) 338-6716
565 Lakeview Pkwy | Fax: (847) 573-1658 | www.objectmentor.com
Suite 135 | | www.XProgramming.com
Vernon Hills, IL, | Training and Mentoring | www.junit.org
60061 | OO, XP, Java, C++, Python | http://fitnesse.org
 
4

4Space

How should I tackle the registration? Use static initialisation in the
Do the registration in 'main'.

Because we have an array of reusable components, we're very careful of what
we expose. There are many layers between the concrete algorithm and main. If
we adopted this approach, it would gimp the component's reusability. i.e. It
couldn't stand alone as a component, it would need all users of this
component to add code to initialise a low level component, a component that
they shouldn't really know exists.

Cheers,

4Space
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top