Name-Based Object Creation

M

Mike Monagle

I'm working on the client side of a distributed application that uses what I
call an XML over quasi-HTTP protocol. The client and server are connected
with a single persistent socket and exchange messages that look something
like this:

Content-type: P51Mustang
Content-length: 147

<?xml version="1.0"?>
<P51Mustang>
<Pilot>SGT Yeager</Pilot>
<Fuel units="gallons">600</Fuel>
<Guns caliber="50">6</Guns>
</P51Mustang>

After parsing the HTTP-like headers, the problem boils down to how to
deserialize this object give its name in the Content-type string. In other
words, how to go about creating an object of type class P51Mustang given
that the message is a "P51Mustang" message.

I'm considering a number of ways to do this and wondered if anyone else has
any different ideas. So far I've come up with:

1. A BIG if-else if-else statement that considers all possible object types:
// maintenance nightmare, I know.
if(contenttype == "P51Mustang")
p = new P51Mustang;
2. A "map" object, using STL or another library, that maps a string to a
function pointer to create the appropriate type of object. The map object
must be populated (hard-coded) to map the names of the various types of
objects to the appropriate function pointers before being used. Another
maintenance nightmare, much like the BIG if-else if- else statement above.
Use would go something like this:
typedef IncomingMessage* (*fpCreateMsg)( );
void *fpv = 0;
if(map.Lookup(contenttype, fpv))
{
// fpv now points to a function will create the appropriate object
type.
fpCreateMsg fp = (fpCreateMsg)fpv; // now we have a function pointer
that can be invoked.
IncomingMessage *pMessage = (*fp)( );
// We now have an uninitialized P51Mustang object, although the
calling object only knows that it's an IncomingMessage.
// pMessage can be use to initialize the object with the XML here.
// Getting the right thing to happen next is a double-dispatch
problem, but I don't want to get into that here.
}
3. Use a DLL (running on Windows) to find the appropriate function based on
contenttype. Easier maintenance, but might be slower than above methods:
void *fpv = GetProcAddress(hModule, contenttype);
if(fpv)
{
// we can proceed as in 2. above if Lookup succeeds.
4. A combination of 2. and 3. above where the function pointer is retrieved
from the DLL if it is not found in the map and then stored in the map for
later (and faster) use. Easier maintenance than 1. and 2. above, and
probably pretty fast.

Any other ideas? I realize that there are many middleware solutions as well
as commercially available and shareware libraries that deal with such object
deserializations over a network, but unfortunately I am not able to use any
of these do to management decisions.

Thanks!

Mike
 
G

Gianni Mariani

Mike said:
I'm working on the client side of a distributed application that uses what I
call an XML over quasi-HTTP protocol. The client and server are connected
with a single persistent socket and exchange messages that look something
like this:

....

Check out "factory pattern".

An implementation is :
http://austria.sourceforge.net/dox/html/group__GenericFactories.html

(shameless plug)

Download the Austria source, check out the factory test case.
 
P

Phlip

Mike said:
typedef IncomingMessage* (*fpCreateMsg)( );
void *fpv = 0;
if(map.Lookup(contenttype, fpv))
{
// fpv now points to a function will create the appropriate object
type.
fpCreateMsg fp = (fpCreateMsg)fpv; // now we have a function
pointer

Why is there a typecast there? If you use STL map, and if all creators have
the same signature, you won't need a typecast.

Crack the book /Design Patterns/, and look at the Prototype and Factory
patterns for your system. The most important thing to resolve is how to add
new items. A big if-then-else chain won't make that easy. The Prototype
pattern can decouple all the types from their own list.
 
M

Mike Monagle

Why is there a typecast there? It's because the sample I concocted for
myself used MFC and not STL. If I had used STL for my sample, and I may end
up using STL eventually, the typecast will not be required.

Yes, my problem could definitely fall into the Factory pattern, or even the
Prototype pattern.

So I cracked "the book" and found that it had to say this about the Factory
Method:

Product* Creator::Create::Create(ProductId id)
{
if(id == MINE)
return new MyProduct:
else if(id == YOURS)
return new YourProduct;
// and so on....
else
return NULL;
)

That's fine. It's just that my ProductId is a String and there are dozens
of different Product(s) that may be created. The Factory Method is fine.
It just makes me wonder about what the best way to implement this.

As far as the Prototype method goes, "the book" has this to say about it:

"A prototype manager is an associative store that returns the prototype
matching a given key." My question then becomes "What is the best way to
implement such an associative store given that the key is a string and there
are dozens or key-prototype pairs?"

Thanks,

Mike
 
P

Phlip

Mike said:
"A prototype manager is an associative store that returns the prototype
matching a given key." My question then becomes "What is the best way to
implement such an associative store given that the key is a string and there
are dozens or key-prototype pairs?"

#include <map>

class
clonable
{
public:
virtual clonable *clone() = 0;
virtual string weBe() = 0;
};

class
red: public clonable
{
public:
virtual string weBe() { return "red"; }
clonable * clone() { return new red(*this); }
};

class
green: public clonable
{
public:
virtual string weBe() { return "green"; }
clonable * clone() { return new green(*this); }
};

class
blue: public clonable
{
public:
virtual string weBe() { return "blue"; }
clonable * clone() { return new blue(*this); }
};


TEST_(TestCase, clonable)
{

red aRed; green aGreen; blue aBlue;
typedef std::map<string, clonable*> clonables_t;
clonables_t clonables;
clonables[aRed .weBe()] = &aRed ;
clonables[aGreen.weBe()] = &aGreen;
clonables[aBlue .weBe()] = &aBlue ;

clonable * clone = clonables["blue"]->clone();
CPPUNIT_ASSERT_EQUAL("blue", clone->weBe());
delete clone;

clone = clonables["red"]->clone();
CPPUNIT_ASSERT_EQUAL("red", clone->weBe());
delete clone;

clone = clonables["green"]->clone();
CPPUNIT_ASSERT_EQUAL("green", clone->weBe());
delete clone;

}

Notice something very important. I could export 'clonables' to any module,
and that module would register its clonable things with it. This system does
not require the module owning 'clonables' to import every module in a
system. The leaf types - red, green, blue - are all decoupled from each
other. Each only needs to know about itself and 'clonables'. This represents
the Dependency Inversion Principle.
 
T

tom_usenet

Why is there a typecast there? It's because the sample I concocted for
myself used MFC and not STL. If I had used STL for my sample, and I may end
up using STL eventually, the typecast will not be required.

Yes, my problem could definitely fall into the Factory pattern, or even the
Prototype pattern.

So I cracked "the book" and found that it had to say this about the Factory
Method:

Product* Creator::Create::Create(ProductId id)
{
if(id == MINE)
return new MyProduct:
else if(id == YOURS)
return new YourProduct;
// and so on....
else
return NULL;
)

That's fine. It's just that my ProductId is a String and there are dozens
of different Product(s) that may be created. The Factory Method is fine.
It just makes me wonder about what the best way to implement this.

As far as the Prototype method goes, "the book" has this to say about it:

"A prototype manager is an associative store that returns the prototype
matching a given key." My question then becomes "What is the best way to
implement such an associative store given that the key is a string and there
are dozens or key-prototype pairs?"

The prototype method doesn't look like the ideal choice here, since
that is better when you need to clone some prototype products, rather
than just create new ones. Something like this:

template <class Base, class Derived>
Base* create()
{
return new Derived;
}

//id to creation function map:
std::map<ProductId, Product* (*)()> m_products;

template <class AProduct>
void Creator::RegisterProduct(ProductId id)
{
m_products[id] = &create<Product, AProduct>;
}

Product* Creator::Create::Create(ProductId id)
{
map_t::const_iterator i = m_products.find(id);
if (i == m_products.end())
return NULL;

return i->second();
}

Then you register a product with:

mycreator.RegisterProduct<Spanner>(Spanner::id); //or similar

and create it with:

Product* p = mycreator.Create(anid);

There are lots of variations; a description of a generic
implementation can be found in Alexandrescu's book, "Modern C++
Design".

Tom
 

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,774
Messages
2,569,598
Members
45,144
Latest member
KetoBaseReviews
Top