Design pattern question

J

Joe Van Dyk

I'm certain that there's some type of a design pattern that neatly
solves my problem, but I dunno what it is.

I need to create Message objects based on on the contents of a string
(that's actually in xml format, but it doesn't really matter).

So, here's an example string (totally unrelated to the actual problem
domain):

Message Type: Dinner
Main Course: Rib Eye Steak
Wine: Red

Another message:

Message Type: Breakfast
Main Course: Eggs
Juice: Orange


So, the structure of each message string contains some similarities and
some differences.

Based on the string, I'd like to create objects that represent each
message. I'm guessing that I'd want some base Message (or, in this
case, Meal) object, and then have classes inherit from that base class
that have their own specific data members.

Would I have some type of a factory that takes a string and returns a
pointer to a Meal object? How could I properly examine the string and
create the proper object inside that factory?

I'm used to Ruby, where this type of thing is fairly easy. But no Ruby
for me.
 
P

Phlip

Joe said:
How could I properly examine the string and
create the proper object inside that factory?

You can just do it stupidly. Because it's a factory, the stupid part is in
only one place.

Well, that's your problem. You are spoiled by doing things smartly. Here's
the stupid way:

if ("lunch" == typeString)
return auto_ptr<Meal>(new Lunch(message));
if ("dinner" == typeString)
return auto_ptr<Meal>(new Dinner(message));
...

The only drawback (besides not being in Ruby) is that system couples the
factory to every meal type. The next step is the Prototype Pattern, like
this:

map<string, Meal>

Now each Meal has a Clone() method, and each .cpp file that implements a
derivative of Meal will use a Register method (called before main()) to add
its type to the map. Then your messages will look a meal up in the map,
Clone it, and use it.

Ultimately, you have a "boundary" situation in your object model. Outside
the boundary, you have raw data, and you need a factory of some type to
upgrade the data into responsible objects. Once inside the boundary, your
objects can call each others virtual methods freely without any 'if' or
'switch' statements (or the equivalents) to detect their types.
 
M

mlimber

Joe said:
I'm certain that there's some type of a design pattern that neatly
solves my problem, but I dunno what it is.

I need to create Message objects based on on the contents of a string
(that's actually in xml format, but it doesn't really matter).

So, here's an example string (totally unrelated to the actual problem
domain):

Message Type: Dinner
Main Course: Rib Eye Steak
Wine: Red

Another message:

Message Type: Breakfast
Main Course: Eggs
Juice: Orange


So, the structure of each message string contains some similarities and
some differences.

Based on the string, I'd like to create objects that represent each
message. I'm guessing that I'd want some base Message (or, in this
case, Meal) object, and then have classes inherit from that base class
that have their own specific data members.

Would I have some type of a factory that takes a string and returns a
pointer to a Meal object? How could I properly examine the string and
create the proper object inside that factory?

I'm used to Ruby, where this type of thing is fairly easy. But no Ruby
for me.

If your objects have the same number and type (std::string?) of
parameters, you could easily do something with an object factory
pattern, like the one from _Modern C++ Design_ (source code at
http://loki-lib.sourceforge.net). See an example in either of these two
posts:

http://groups.google.com/group/comp.lang.c++/msg/ed0274aba1a1b04c

http://groups.google.com/group/comp.lang.c++/msg/e8d0d7f5d2dd6126

Cheers! --M
 
N

Noah Roberts

Joe said:
I'm certain that there's some type of a design pattern that neatly
solves my problem, but I dunno what it is.

Try your question in comp.object - it is a large part of what the group
is about.
 
J

Joe Van Dyk

Phlip said:
Joe Van Dyk wrote:




You can just do it stupidly. Because it's a factory, the stupid part is in
only one place.




Well, that's your problem. You are spoiled by doing things smartly. Here's
the stupid way:

if ("lunch" == typeString)
return auto_ptr<Meal>(new Lunch(message));
if ("dinner" == typeString)
return auto_ptr<Meal>(new Dinner(message));
...

I'm going with this method first, as the GoF description for Prototype
makes my head hurt.

When I return a pointer to a Meal object, how does the client code know
what type of an object the pointer points to?

MealFactory f;
std::auto_ptr<Meal> meal(f.create_message(lunch_meal_string));
std::cout << "some function that's not in the Meal class: "
<< meal->lunch_drink_method() << std::endl;

That's some test code I have, and it's complaining that
lunch_drink_method() isn't declared -- but it's a method in the Lunch class.

Do I need to cast or something here?

Joe
 
J

Joe Van Dyk

Joe said:
I'm going with this method first, as the GoF description for Prototype
makes my head hurt.

When I return a pointer to a Meal object, how does the client code know
what type of an object the pointer points to?

MealFactory f;
std::auto_ptr<Meal> meal(f.create_message(lunch_meal_string));
std::cout << "some function that's not in the Meal class: "
<< meal->lunch_drink_method() << std::endl;

That's some test code I have, and it's complaining that
lunch_drink_method() isn't declared -- but it's a method in the Lunch
class.

Do I need to cast or something here?

Hm, something like this works:

std::auto_ptr<Lunch> meal(dynamic_cast<Lunch*>
(f.create_message(lunch_meal_string).release()) );

But that makes me want to cry.
 
D

davidrubin

Joe said:
I'm certain that there's some type of a design pattern that neatly
solves my problem, but I dunno what it is.

I need to create Message objects based on on the contents of a string
(that's actually in xml format, but it doesn't really matter).

So, here's an example string (totally unrelated to the actual problem
domain):

Message Type: Dinner
Main Course: Rib Eye Steak
Wine: Red

Another message:

Message Type: Breakfast
Main Course: Eggs
Juice: Orange


So, the structure of each message string contains some similarities and
some differences.

Based on the string, I'd like to create objects that represent each
message. I'm guessing that I'd want some base Message (or, in this
case, Meal) object, and then have classes inherit from that base class
that have their own specific data members.

Would I have some type of a factory that takes a string and returns a
pointer to a Meal object? How could I properly examine the string and
create the proper object inside that factory?

IOne solution I've seen for dealing with this problem to create a
discrimintated union of all message types, and then construct a bunch
of utility function templates for operating on the messages. What you
do *not* want to do here is build a large hierarchy of objects related
by structural inheritance. This kind of solution is very brittle, and
does not scale well.
 
P

Phlip

To do Prototype in Ruby, you can call

name = "Lunch"
....
eval name+".new"

or you can call

klass = Lunch
....
klass.new

or you can... etc.
Hm, something like this works:

std::auto_ptr<Lunch> meal(dynamic_cast<Lunch*>
(f.create_message(lunch_meal_string).release()) );

But that makes me want to cry.

Stuff it all down into a template with a clean interface.

In C++, because classes are not themselves objects, you must use templates
to abstract one stretch of code across many class types.
 
J

Joe Van Dyk

Phlip said:
Joe Van Dyk wrote:




To do Prototype in Ruby, you can call

name = "Lunch"
...
eval name+".new"

or you can call

klass = Lunch
...
klass.new

or you can... etc.

Oh, that makes sense. Rewriting the GoF book in Ruby probably would cut
the size of the book down by 90%, at least.
Stuff it all down into a template with a clean interface.

In C++, because classes are not themselves objects, you must use templates
to abstract one stretch of code across many class types.


But how do I know what object to cast the returned Meal* to? Once I
create the object based off the incoming Meal specification string, I
need to do different things based on the type of Meal.

Joe
 
J

Joe Van Dyk

IOne solution I've seen for dealing with this problem to create a
discrimintated union of all message types, and then construct a bunch
of utility function templates for operating on the messages. What you
do *not* want to do here is build a large hierarchy of objects related
by structural inheritance. This kind of solution is very brittle, and
does not scale well.


Sorry -- I'm dumb. What's a "discriminated union"? And how would it
help me?

I don't have a particularly large hierarchy of classes here. I'm just
looking for something that:

a. Makes it painfully obvious how the system works
b. Easy to test (unit and otherwise)
c. Somewhat easy to maintain and extend, but that's secondary to the
first two. It doesn't need to be perfectly and painlessly extendable
for every type of Meal in the galaxy.

Joe
 
P

Phlip

Joe said:
But how do I know what object to cast the returned Meal* to? Once I
create the object based off the incoming Meal specification string, I need
to do different things based on the type of Meal.

That's what OO is all about. After you get a Meal *, you can call
pMeal->eat(), and this will dynamically dispatch to either
Breakfast::Meal(), or Brunch::Meal(), or PreLunch::Meal(), or Lunch::Meal(),
or Twozies::Meal(), or TeaTime::Meal(), or Appetizer::Meal(), or ...

You get the idea. Put the behavior in different classes, behind a common
interface, and then program to the interface. That's what the book /Design
Patterns/ teaches (_and_ it's what Ruby teaches!)
 
J

joevandyk

Phlip said:
That's what OO is all about. After you get a Meal *, you can call
pMeal->eat(), and this will dynamically dispatch to either
Breakfast::Meal(), or Brunch::Meal(), or PreLunch::Meal(), or Lunch::Meal(),
or Twozies::Meal(), or TeaTime::Meal(), or Appetizer::Meal(), or ...

You get the idea. Put the behavior in different classes, behind a common
interface, and then program to the interface. That's what the book /Design
Patterns/ teaches (_and_ it's what Ruby teaches!)

That works if the interface is exactly the same. But there's going to
be methods and data structures in some of the Brunches and Appetizers
classes that aren't in others. At this point, I'm afraid my analogy is
breaking down. But I'll keep at it.

Say I receive a Brunch message. I create a Brunch object. Then I need
to do Brunch-specific things -- and those things are fairly different
from the rest of the different Meals.

Oh, perhaps I'll make a Meal::Eat() method (and over-ride that in each
subclass). That method will contain all the stuff I'm supposed to do
when it's time to eat a new meal. That should've been obvious. God
I'm dumb.
 
J

Joe Van Dyk

Phlip said:
Joe Van Dyk wrote:




You can just do it stupidly. Because it's a factory, the stupid part is in
only one place.




Well, that's your problem. You are spoiled by doing things smartly. Here's
the stupid way:

if ("lunch" == typeString)
return auto_ptr<Meal>(new Lunch(message));
if ("dinner" == typeString)
return auto_ptr<Meal>(new Dinner(message));
...

The only drawback (besides not being in Ruby) is that system couples the
factory to every meal type. The next step is the Prototype Pattern, like
this:

map<string, Meal>

Now each Meal has a Clone() method, and each .cpp file that implements a
derivative of Meal will use a Register method (called before main()) to add
its type to the map. Then your messages will look a meal up in the map,
Clone it, and use it.

Ultimately, you have a "boundary" situation in your object model. Outside
the boundary, you have raw data, and you need a factory of some type to
upgrade the data into responsible objects. Once inside the boundary, your
objects can call each others virtual methods freely without any 'if' or
'switch' statements (or the equivalents) to detect their types.

I take it the Prototype pattern and the Factory pattern are really similar?
 
D

Diego Martins

Joe said:
Oh, that makes sense. Rewriting the GoF book in Ruby probably would cut
the size of the book down by 90%, at least.

this is an excellent idea! GoF design patterns applied in the most
popular programming languages (C++, Java, Python, Ruby and php 5)

who will dare to write such a book? :)
 
N

Noah Roberts

Phlip said:
That's what OO is all about. After you get a Meal *, you can call
pMeal->eat(), and this will dynamically dispatch to either
Breakfast::Meal(), or Brunch::Meal(), or PreLunch::Meal(), or Lunch::Meal(),
or Twozies::Meal(), or TeaTime::Meal(), or Appetizer::Meal(), or ...

Actually, Meal* would be a parameter to the animal eating the meal.
This would probably call a mouth->chew(mealItem) which would ask the
meal item for various details including pressure required to
crush...etc...and calculate the chewing time and method to use for an
item with such characteristics.

Then eat() would likely call swallow, which would calculate how chewed
the item is and how easy it goes down...or it would be passed that
information from the mouth...or possibly crushing food would change its
state into something like mush, which would have the information
required to find out how easy it goes down....food->crush(pressure,
grindRadius)...the mouth would know what kind of teeth it has, if any,
and be able to call the correct action on the food...cut, strain,
crush...

Then eat() would call stomach->beginDigestion() which would call
various functions on the meal to break it down and get the components
necissary to pass to smallIntestine->digest(). Meal would likely be
destroyed at this point as it has been mashed and stewed into other
things that would have different sets methods they respond to.
Perhapse something like complexCarb->extractGlucose(amino_count).

If meal had an eat() then it would need to know details about the thing
eating it...this isn't good. For instance, some animals eat and shit
out of the same orifice. Some animals have teeth, others
don't...etc... If meal had an eat it would need subclasses for each
animal type and characteristic so it could call the appropriate eating
mechanisms. If the animal eats the meal on the other hand
(animal->eat(meal)) it just needs to check the charactaristics of the
meal to see if it can be eaten (ie animals that eat stones, like birds,
could easily do so but animals with teeth would call a check to see the
pressure required and realize it couldn't devour that item
correctly..even if it could be swallowed and passed through). Also,
chemical content of the meal would require a check for each animal
because some die when eating things others can eat just fine. For
instance, some people are deathly alergic to strawberries...the animal
can check the chem content and compare to its own poisons
database...which could be dynamic.
 
N

Noah Roberts

Noah said:
Actually, Meal* would be a parameter to the animal eating the meal.
This would probably call a mouth->chew(mealItem) which would ask the
meal item for various details including pressure required to
crush...etc...and calculate the chewing time and method to use for an
item with such characteristics.

Then eat() would likely call swallow, which would calculate how chewed
the item is and how easy it goes down...or it would be passed that
information from the mouth...or possibly crushing food would change its
state into something like mush, which would have the information
required to find out how easy it goes down....food->crush(pressure,
grindRadius)...the mouth would know what kind of teeth it has, if any,
and be able to call the correct action on the food...cut, strain,
crush...

Then eat() would call stomach->beginDigestion() which would call
various functions on the meal to break it down and get the components
necissary to pass to smallIntestine->digest(). Meal would likely be
destroyed at this point as it has been mashed and stewed into other
things that would have different sets methods they respond to.
Perhapse something like complexCarb->extractGlucose(amino_count).

If meal had an eat() then it would need to know details about the thing
eating it...this isn't good. For instance, some animals eat and shit
out of the same orifice. Some animals have teeth, others
don't...etc... If meal had an eat it would need subclasses for each
animal type and characteristic so it could call the appropriate eating
mechanisms. If the animal eats the meal on the other hand
(animal->eat(meal)) it just needs to check the charactaristics of the
meal to see if it can be eaten (ie animals that eat stones, like birds,
could easily do so but animals with teeth would call a check to see the
pressure required and realize it couldn't devour that item
correctly..even if it could be swallowed and passed through). Also,
chemical content of the meal would require a check for each animal
because some die when eating things others can eat just fine. For
instance, some people are deathly alergic to strawberries...the animal
can check the chem content and compare to its own poisons
database...which could be dynamic.

The point of all that of course is so that nothing need be dispatched
or cast. Establish what information is needed about a food by the
eater and that establishes your base class interface. Implement this
interface for all the different meals. Same with the eater...only its
main interface, eat(), is much simpler and will then delegate the
different aspects of the eat algorithm to different parts of its
anatomy. The eat() function itself could even be an abstract algorithm
since the basic method of digestion is very similar for a wide variety
of eaters...some will be different enough though that an algorithm of
their own is required.
 
P

Phlip

Noah said:
The point of all that of course is so that nothing need be dispatched
or cast.

Thank you. For a minute there, I thought you were wakin' and bakin', and
caught the munchies or something.
 
N

Noah Roberts

Noah said:
Actually, Meal* would be a parameter to the animal eating the meal.
This would probably call a mouth->chew(mealItem) which would ask the
meal item for various details including pressure required to
crush...etc...and calculate the chewing time and method to use for an
item with such characteristics.

On the other hand, meal as described by the OP is a different deal. It
also should be a parameter though since people eat things in different
orders. Perhapse meal has an interface to get the main course, the
drink, desert, etc...then you might need a server object that passes
the different parts of the meal at the appropriate time...

Server::serve(Meal * meal)
{
deliver(table, patron, meal->appetizer())
wait...
deliver(table, patron, meal->main())
....
}

These things are foods that the patron eat()'s.
 
R

red floyd

Diego said:
this is an excellent idea! GoF design patterns applied in the most
popular programming languages (C++, Java, Python, Ruby and php 5)

who will dare to write such a book? :)

I've got one (for Java at least) -- "Your Brain on Design Patterns" or
some such title.
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top