By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,635 Members | 2,174 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,635 IT Pros & Developers. It's quick & easy.

An Elegant Design/Solution?

P: n/a
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help
me with. It is related to my previous post but since it was pointed out that
I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a
spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve,
but I have reduced it to as simple a concept as I can.

Here are my requirements:
1) There is a class CFood which implements a generic food class and will be
subclassed. Each foodtype will have its own properties, etc making good use
of polymorphism where appropriate (honest).
2) CFood objects will be stored, generically, in lists. For example, a class
CPantry may contain a std::list of CFood pointers.
3) There is a class CLifeform which will also be subclassed (e.g. CMonster
or CHumanoid). Food may be taken from one of the lists/collections and
offered to a lifeform. Each CLifeform-based class may process or eat
CFood-derived objects differently. For example, CHumanoid-based classes may
eat CCookie objects whereas CMonster-based classes may eat other lifeforms.
4) Other classes (not CLifeform-drived) may also eat/handle CFood objects.

My current 'solution' looks like this:

/////////////////////////////////////////////////////
// Monster Munch

#include "stdafx.h"
#include <list>

class CFood
{
public:
CFood() {}
virtual ~CFood() {}
};

class CFoodCookie : public CFood
{
public:
CFoodCookie() {}
virtual ~CFoodCookie() {}

// Cookie properties...
};

class CFoodHorse : public CFood
{
public:
CFoodHorse() {}
virtual ~CFoodHorse() {}

// Horse properties...
};

class CLifeform
{
public:
CLifeform() {}
virtual ~CLifeform() {}

public:
virtual bool Feed(CFood* pFood) {printf("\nI don't know what this is!");
return false;}
virtual bool Feed(CFoodCookie* pFood) {printf("\nI don't eat cookies!");
return false;}
virtual bool Feed(CFoodHorse* pFood) {printf("\nI don't eat horses!");
return false;}
};

class CHumanoid : public CLifeform
{
public:
CHumanoid() {}
virtual ~CHumanoid() {}

virtual bool Feed(CFoodCookie* pCookie) {delete pCookie; printf("\nYummy! A
cookie!"); return true;}
};

class CMonster : public CLifeform
{
public:
CMonster() {}
virtual ~CMonster() {}

virtual bool Feed(CFoodHorse* pHorse) {delete pHorse; printf("\nI was so
hungry, I ate a horse!"); return true;}
};

class CPantry
{
public:
CPantry() {}
~CPantry() {}

void AddFood(CFood* pFood)
{
m_Stock.push_back(pFood);
}

void Feed(CLifeform& Lifeform)
{
while (m_Stock.size())
{
// Get the food...
CFood* pFood = m_Stock.front();
m_Stock.pop_front();

if (!Lifeform.Feed(pFood))
{
// Throw the food away
delete pFood;
}
}
}

private:
std::list<CFood*> m_Stock;
};

int main()
{
CPantry ThePantry;

// Let's see what we have:
// Two cookies...
ThePantry.AddFood(new CFoodCookie());
ThePantry.AddFood(new CFoodCookie());
// Goodness knows what this is...
ThePantry.AddFood(new CFood());
// And this, apparently...
ThePantry.AddFood(new CFoodHorse());

// Feed the guest...
CMonster ScaryDude;
ThePantry.Feed(ScaryDude);

getchar();

return 0;
}

/////////////////////////////////////////////////////

There are pros and cons to this solution, as I see it. These are...

Pros:
1. CLifeform derived classes need only override the 'handlers' for the foods
they wish to eat.
2. Each Feed() method has a pointer to the specific object type to be
handled appropriately.

Cons:
1. It doesn't work! I know exactly why it doesn't work, CFood* is always
passed to the CLifeform class, so the lifeform never knows what it is being
fed. The design change (or redesign) must allow pointers to specific
CFood-derived classes to be passed around.
2. CLifeform has to know (beforehand) of all food types that could ever be
handled by subclasses...however, I also see this as desirable...

Regarding Con1, I can conceive of one way around this. Perhaps CFood could
be given a virtual method such as:
bool CFood::FeedLifeform(CLifeform& Lifeform)
{
Lifeform.Feed(this);
}

However, each CFood-derived object would need to override this, but
implemented with exactly the same code! First, CFood classes will be added
frequently, so I wish to minimise the code involved in implementing a
subclass. Second, this would require that CFood knew explicitly that it may
be used to feed CLifeform classes. I don't want the class to know that and,
as stated, CFood classes may be handled by non-CLifeform-derived classes
also - more CFood overhead.

I also realise that each CLifeform-derived class could examine a generic
CFood pointer and determine what to do with the food according to its type.
However, I really like the idea of CLifeform subclasses just overriding the
food handlers they are interested in.

Given the requirements and preferences, can anyone suggest anything
fruitful?

Many many thanks,
Lucy x
Jul 19 '05 #1
Share this Question
Share on Google+
9 Replies


P: n/a

"Patchwork" <ID***********@IDontLike.Spam> wrote in message
news:bp**********@hercules.btinternet.com...
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help me with. It is related to my previous post but since it was pointed out that I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a
spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve, but I have reduced it to as simple a concept as I can.

Here are my requirements:
1) There is a class CFood which implements a generic food class and will be subclassed. Each foodtype will have its own properties, etc making good use of polymorphism where appropriate (honest).
2) CFood objects will be stored, generically, in lists. For example, a class CPantry may contain a std::list of CFood pointers.
3) There is a class CLifeform which will also be subclassed (e.g. CMonster
or CHumanoid). Food may be taken from one of the lists/collections and
offered to a lifeform. Each CLifeform-based class may process or eat
CFood-derived objects differently. For example, CHumanoid-based classes may eat CCookie objects whereas CMonster-based classes may eat other lifeforms. 4) Other classes (not CLifeform-drived) may also eat/handle CFood objects.
[SNIP] Pros:
1. CLifeform derived classes need only override the 'handlers' for the foods they wish to eat.
2. Each Feed() method has a pointer to the specific object type to be
handled appropriately.

Cons:
1. It doesn't work! I know exactly why it doesn't work, CFood* is always
passed to the CLifeform class, so the lifeform never knows what it is being fed. The design change (or redesign) must allow pointers to specific
CFood-derived classes to be passed around.
2. CLifeform has to know (beforehand) of all food types that could ever be
handled by subclasses...however, I also see this as desirable...

Regarding Con1, I can conceive of one way around this. Perhaps CFood could
be given a virtual method such as:
bool CFood::FeedLifeform(CLifeform& Lifeform)
{
Lifeform.Feed(this);
}

However, each CFood-derived object would need to override this, but
implemented with exactly the same code! First, CFood classes will be added
frequently, so I wish to minimise the code involved in implementing a
subclass. Second, this would require that CFood knew explicitly that it may be used to feed CLifeform classes. I don't want the class to know that and, as stated, CFood classes may be handled by non-CLifeform-derived classes
also - more CFood overhead.

I also realise that each CLifeform-derived class could examine a generic
CFood pointer and determine what to do with the food according to its type. However, I really like the idea of CLifeform subclasses just overriding the food handlers they are interested in.


Hi Lucy,

it looks to me as if you're heading for a generic visitor implementation.
First of all regarding your CFood class I think there is no reason to allow
instances of CFood objects. Therefore you might consider preventing the
creation of CFood objects by declaring the necessary functions (ctor, copy
ctor....) protected instead of public. Furthermore I'd propose the visitor
pattern for your problem. In principle every food object can be regarded as
a visitable object and every Lifeform as the corresponding visitor. This
means that each food object will accept a visitor and trigger the visit
function of the visitor with itself passed as an argument.

For example:

class CCookie : public CFood, {
public:
void Visit( CLifeform& Visitor ) {
Visitor.Visit( this );
}
}

class CMonster : public CLifeForm {
public:
void Visit( CCookie& Visitable) {
cout << "I hate cookies" << endl;
}
}

class CHuman: public CLifeForm {
public:
void Visit( CCookie& Visitable) {
cout << "Yummie - I love cookies!" << endl;
}
}

As you can see it doesn't matter to the cookie what kind of lifeform takes
care of it. Still this would include a lot of code you might have to write.
Thus there is some easier solution using a generic visitor implementation
with some template magic involved. Using the my code below you just have to
do the following steps to create a visitor pattern.

1. Derive a concrete visitor from CBaseVisitor & CVisitor<VisitedElements>
2. Implement the Visit( VisitedElement& ) functions

For example:

class CMonster: public CLifeForm, public CBaseVisitor, // REQUIRED!!!
public CVisitor<CCookie>
public CVisitor<CSoup>
{
public:

void Visit( CCookie& Food) { std::cout << "Got some cookie" <<
std::endl; };
void Visit( CSoup& Food) { std::cout << "Got some soup" << std::endl; };
};

class CMonster: public CLifeForm, public CBaseVisitor, // REQUIRED!!!
public CVisitor<CCookie>
{
public:

void Visit( CCookie& Food) { std::cout << "Got some cookie" <<
std::endl; };
};
3. Derive a visitable class from CBaseVisitable
4. Add IMPLEMENT_VISITABLE() macro call to the class.

class CCookie: public CFood, public CBaseVisitable {
public:
IMPLEMENT_VISITABLE() // Adds inline version of the Accept function
//...
};

Now you just have to iterator over the food container passing the respective
lifeform to its Visit function and that's it. The nice thing with this
solution is that it doesn't matter what kind of visitor you pass to your
food because the visitor is capable of checking whether it accepts the food
or not. This is done by deriving each visitor from a CVisitor<> class with
the visitable class type as a template parameter. Thus it can check using
dynamic_cast whether it can process the passed class type or not.

HTH
Chris
And here is the code:

//////////////////////////////////////////////////////////////////////
//
// Visitor.h: interface for the Visitor pattern.
//
// Implementation based on a design by Andrei Alexandrescu
//
// ATTENTION: RTTI MUST be enabled!!! (/GR switch using MSVC)
//
// Usage:
//
// 1. Derive a concrete visitor from CBaseVisitor &
CVisitor<VisitedElements>
// 2. Implement the Visit( VisitedElement& ) functions
// 3. Derive a visitable class from CBaseVisitable
// 4. Add IMPLEMENT_VISITABLE() macro call to the class.
//
// Example:
//
// class CLine: public CBaseVisitable {
// public:
// IMPLEMENT_VISITABLE() // Adds inline version of the Accept function
//
// };
//
// class COpenGLView: public CBaseVisitor, // REQUIRED!!!
// public CVisitor<CLine>
// {
// public:
//
// void Visit( CLine& Line ) { std::cout << "Line visited" <<
std::endl; };
//
// };
//
// (c) 2003 Chris Theis
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_VISITOR_H__02263040_B6FA_46A6_AB15_9E 47F23A70BA__INCLUDED_)
#define AFX_VISITOR_H__02263040_B6FA_46A6_AB15_9E47F23A70B A__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define IMPLEMENT_VISITABLE() virtual void Accept( CBaseVisitor& Guest ) {
AcceptImpl( *this, Guest ); };

class CBaseVisitor
{
public:
virtual ~CBaseVisitor() {};
};

template< class T>
class CVisitor
{
public:

// must be implemented by the derived classes with regard to T
virtual void Visit( T& ) = 0;
};

class CBaseVisitable
{
public:

virtual ~CBaseVisitable() {};

// Must be overloaded by adding the IMPLEMENT_VISITABLE to the
// derived classes. This way the necessary type information
// is automatically supplied because the *this pointer has
// the correct (derived) type!

virtual void Accept( CBaseVisitor& ) = 0;

protected:

// The actual implementation of the accept function that is called
// with the appropriate type information due to the IMPLEMENT_VISITABLE
macro
template< class T >
static void AcceptImpl( T& Visited, CBaseVisitor& Guest ) {

// Check if the passed CBaseVisitor is also a CVisitor<T> object
// because CVisitor<T> provides the Visit function!
if( CVisitor<T>* pPtr = dynamic_cast<CVisitor<T>*>( &Guest) ) {
pPtr->Visit( Visited );
}
};
};

#endif //
Jul 19 '05 #2

P: n/a
Patchwork wrote:
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help
me with. It is related to my previous post but since it was pointed out that
I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a
spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve,
but I have reduced it to as simple a concept as I can.


In cases like this, I have created a base class that has a method for
all different types of food.

e.g.

class CFoodCookie;
class CFoodHorse; // Cheval ?

class CFoodProcessor
{
public:

virtual bool Process( CFoodCookie * food )
{
return false;
}
virtual bool Process( CFoodHorse * food )
{
return false;
}

// etc - for each kind of food
};

// in the derived form of of each food there is a
// ProcessThis method

class CFood
{
virtual bool ProcessWith( CFoodProcessor & processor ) = 0;
};

// A cookie's processor gets passed a cookie.
//
class CFoodCookie : public CFood
{
bool ProcessWith( CFoodProcessor & processor );
{
return processor->Process( this );
}
};

//
// Then anything that needs to process food
// gets to inherit CFoodProcessor
//
class CLifeForm : public CFoodProcessor
{
// LifeForm is a food processor

void Eat( CFood * food )
{
food->ProcessWith( this );
}

// override the methods in CFoodProcessor that make sense

bool Process( CFoodCookie * food )
{
// YUMMY
return true;
}

};

Jul 19 '05 #3

P: n/a
"Gianni Mariani" <gi*******@mariani.ws> wrote...
Patchwork wrote:
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help me with. It is related to my previous post but since it was pointed out that I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve, but I have reduced it to as simple a concept as I can.


In cases like this, I have created a base class that has a method for
all different types of food.
[...]


The problem, I think, is that if somebody creates another class
descendent from CFood, such base class will not be able to process
it without having to change (at least a little bit).

Given certain limitations (all foods are known at the beginning,
all life forms are known at the beginning), there is no problem,
just make the base class for all life forms process all possible
foods (with default behaviour to reject it) and let every life
form override only those members that accept those (using double
dispatch because the "pantry" returns only a pointer to the base
food class). However, this mechanism is not very well suited
for expansion if needed. Adding foods requires rewriting the
base class for the life forms.

Rewriting the base class for life forms is not such a big deal,
of course. If it's the implementation we're talking about, the
derived classes won't have to change. However, from the design
point of view, it seems that it would be beneficial if some life
forms would be able to accept the new food and derive nutrition
from it simply because it _may_ be nutritious. That's why my
suggestion was to supply some kind of nutrition and taste info
in each food and let creatures decide whether each food is OK
to consume and how much nutrition and pleasure (or danger) to
derive from each food... Makes for more interesting process,
I believe :-)

Victor

Jul 19 '05 #4

P: n/a

"Chris Theis" <Ch*************@nospam.cern.ch> wrote in message
news:bp**********@sunnews.cern.ch...

"Patchwork" <ID***********@IDontLike.Spam> wrote in message
news:bp**********@hercules.btinternet.com...
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help
me with. It is related to my previous post but since it was pointed out

that
I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to

solve,
but I have reduced it to as simple a concept as I can.

Here are my requirements:
1) There is a class CFood which implements a generic food class and will

be
subclassed. Each foodtype will have its own properties, etc making good

use
of polymorphism where appropriate (honest).
2) CFood objects will be stored, generically, in lists. For example, a

class
CPantry may contain a std::list of CFood pointers.
3) There is a class CLifeform which will also be subclassed (e.g. CMonster or CHumanoid). Food may be taken from one of the lists/collections and
offered to a lifeform. Each CLifeform-based class may process or eat
CFood-derived objects differently. For example, CHumanoid-based classes

may
eat CCookie objects whereas CMonster-based classes may eat other

lifeforms.
4) Other classes (not CLifeform-drived) may also eat/handle CFood objects.

[SNIP]
Pros:
1. CLifeform derived classes need only override the 'handlers' for the

foods
they wish to eat.
2. Each Feed() method has a pointer to the specific object type to be
handled appropriately.

Cons:
1. It doesn't work! I know exactly why it doesn't work, CFood* is always
passed to the CLifeform class, so the lifeform never knows what it is

being
fed. The design change (or redesign) must allow pointers to specific
CFood-derived classes to be passed around.
2. CLifeform has to know (beforehand) of all food types that could ever be handled by subclasses...however, I also see this as desirable...

Regarding Con1, I can conceive of one way around this. Perhaps CFood could be given a virtual method such as:
bool CFood::FeedLifeform(CLifeform& Lifeform)
{
Lifeform.Feed(this);
}

However, each CFood-derived object would need to override this, but
implemented with exactly the same code! First, CFood classes will be added frequently, so I wish to minimise the code involved in implementing a
subclass. Second, this would require that CFood knew explicitly that it

may
be used to feed CLifeform classes. I don't want the class to know that

and,
as stated, CFood classes may be handled by non-CLifeform-derived classes
also - more CFood overhead.

I also realise that each CLifeform-derived class could examine a generic
CFood pointer and determine what to do with the food according to its

type.
However, I really like the idea of CLifeform subclasses just overriding

the
food handlers they are interested in.


Hi Lucy,


Hi, thanks for your reply...
it looks to me as if you're heading for a generic visitor implementation.
Hmmm, sounds like a model/architecture I haven't heard of is about to be
described. Excellent, lead on...:-)
First of all regarding your CFood class I think there is no reason to allow instances of CFood objects. Therefore you might consider preventing the
creation of CFood objects by declaring the necessary functions (ctor, copy
ctor....) protected instead of public.
In the example I gave, that's a fair and valuable observation :-)
Furthermore I'd propose the visitor
pattern for your problem. In principle every food object can be regarded as a visitable object and every Lifeform as the corresponding visitor. This
means that each food object will accept a visitor and trigger the visit
function of the visitor with itself passed as an argument.

For example:

class CCookie : public CFood, {
public:
void Visit( CLifeform& Visitor ) {
Visitor.Visit( this );
}
}

class CMonster : public CLifeForm {
public:
void Visit( CCookie& Visitable) {
cout << "I hate cookies" << endl;
}
}
The monster isn't Sesame Street's Cookie Monster then? :-)
class CHuman: public CLifeForm {
public:
void Visit( CCookie& Visitable) {
cout << "Yummie - I love cookies!" << endl;
}
}

As you can see it doesn't matter to the cookie what kind of lifeform takes
care of it. Still this would include a lot of code you might have to write. Thus there is some easier solution using a generic visitor implementation
with some template magic involved.
Yes, it is a direction I was contemplating, but the code overhead seemed
excessive. Reveal the magic :-)
Using the my code below you just have to
do the following steps to create a visitor pattern.

1. Derive a concrete visitor from CBaseVisitor & CVisitor<VisitedElements>
2. Implement the Visit( VisitedElement& ) functions

For example:

class CMonster: public CLifeForm, public CBaseVisitor, // REQUIRED!!!
public CVisitor<CCookie>
public CVisitor<CSoup>
{
public:

void Visit( CCookie& Food) { std::cout << "Got some cookie" <<
std::endl; };
void Visit( CSoup& Food) { std::cout << "Got some soup" << std::endl; }; };

class CMonster: public CLifeForm, public CBaseVisitor, // REQUIRED!!!
public CVisitor<CCookie>
{
public:

void Visit( CCookie& Food) { std::cout << "Got some cookie" <<
std::endl; };
};
Eek! Multiple inheritance :-)
3. Derive a visitable class from CBaseVisitable
4. Add IMPLEMENT_VISITABLE() macro call to the class.

class CCookie: public CFood, public CBaseVisitable {
public:
IMPLEMENT_VISITABLE() // Adds inline version of the Accept function
//...
};

Now you just have to iterator over the food container passing the respective lifeform to its Visit function and that's it. The nice thing with this
solution is that it doesn't matter what kind of visitor you pass to your
food because the visitor is capable of checking whether it accepts the food or not. This is done by deriving each visitor from a CVisitor<> class with
the visitable class type as a template parameter. Thus it can check using
dynamic_cast whether it can process the passed class type or not.
Wow, I see how this works and it isn't where my mind would have taken me.
Thanks for introducing me to this interesting concept, I will be looking
into it further out of interest if nothing else :-) I'll probably take
aspects of your suggestions into a solution but I'm not sure that the
radically different coding approach will fall into place in the existing
project.
HTH
Chris
It has already.
Many thanks,
Lucy x

And here is the code:

//////////////////////////////////////////////////////////////////////
//
// Visitor.h: interface for the Visitor pattern.
//
// Implementation based on a design by Andrei Alexandrescu
//
// ATTENTION: RTTI MUST be enabled!!! (/GR switch using MSVC)
//
// Usage:
//
// 1. Derive a concrete visitor from CBaseVisitor &
CVisitor<VisitedElements>
// 2. Implement the Visit( VisitedElement& ) functions
// 3. Derive a visitable class from CBaseVisitable
// 4. Add IMPLEMENT_VISITABLE() macro call to the class.
//
// Example:
//
// class CLine: public CBaseVisitable {
// public:
// IMPLEMENT_VISITABLE() // Adds inline version of the Accept function //
// };
//
// class COpenGLView: public CBaseVisitor, // REQUIRED!!!
// public CVisitor<CLine>
// {
// public:
//
// void Visit( CLine& Line ) { std::cout << "Line visited" <<
std::endl; };
//
// };
//
// (c) 2003 Chris Theis
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_VISITOR_H__02263040_B6FA_46A6_AB15_9E 47F23A70BA__INCLUDED_) #define AFX_VISITOR_H__02263040_B6FA_46A6_AB15_9E47F23A70B A__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define IMPLEMENT_VISITABLE() virtual void Accept( CBaseVisitor& Guest ) {
AcceptImpl( *this, Guest ); };

class CBaseVisitor
{
public:
virtual ~CBaseVisitor() {};
};

template< class T>
class CVisitor
{
public:

// must be implemented by the derived classes with regard to T
virtual void Visit( T& ) = 0;
};

class CBaseVisitable
{
public:

virtual ~CBaseVisitable() {};

// Must be overloaded by adding the IMPLEMENT_VISITABLE to the
// derived classes. This way the necessary type information
// is automatically supplied because the *this pointer has
// the correct (derived) type!

virtual void Accept( CBaseVisitor& ) = 0;

protected:

// The actual implementation of the accept function that is called
// with the appropriate type information due to the IMPLEMENT_VISITABLE
macro
template< class T >
static void AcceptImpl( T& Visited, CBaseVisitor& Guest ) {

// Check if the passed CBaseVisitor is also a CVisitor<T> object
// because CVisitor<T> provides the Visit function!
if( CVisitor<T>* pPtr = dynamic_cast<CVisitor<T>*>( &Guest) ) {
pPtr->Visit( Visited );
}
};
};

#endif //

Jul 19 '05 #5

P: n/a
Thanks for you post Gianni, you've offered me much more confidence in
continuing down the path I started. Perhaps having the CFood-derived classes
'filtering' the appropriate action to the CFoodProcessor (or other) objects
is indeed the way to proceed.

What's worse, a moster eating a horse (cheval), or putting a horse into a
food processor? :-)

Many thanks,
Lucy x

"Gianni Mariani" <gi*******@mariani.ws> wrote in message
news:bp********@dispatch.concentric.net...
Patchwork wrote:
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help me with. It is related to my previous post but since it was pointed out that I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as a spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve, but I have reduced it to as simple a concept as I can.


In cases like this, I have created a base class that has a method for
all different types of food.

e.g.

class CFoodCookie;
class CFoodHorse; // Cheval ?

class CFoodProcessor
{
public:

virtual bool Process( CFoodCookie * food )
{
return false;
}
virtual bool Process( CFoodHorse * food )
{
return false;
}

// etc - for each kind of food
};

// in the derived form of of each food there is a
// ProcessThis method

class CFood
{
virtual bool ProcessWith( CFoodProcessor & processor ) = 0;
};

// A cookie's processor gets passed a cookie.
//
class CFoodCookie : public CFood
{
bool ProcessWith( CFoodProcessor & processor );
{
return processor->Process( this );
}
};

//
// Then anything that needs to process food
// gets to inherit CFoodProcessor
//
class CLifeForm : public CFoodProcessor
{
// LifeForm is a food processor

void Eat( CFood * food )
{
food->ProcessWith( this );
}

// override the methods in CFoodProcessor that make sense

bool Process( CFoodCookie * food )
{
// YUMMY
return true;
}

};

Jul 19 '05 #6

P: n/a
Hi again Victor,

First, thanks for replying and for taking the time to read my posts again
:-) Your latest comments on the previous thread have been particularly
helpful and encouraging. I certainly like where your mind is going regarding
the food properties and commonalities! It is a good suggestion to provide
handling of unknown and non-implemented food types in lifeform-based
classes.

Thanks to everyone for responding...it has been quite exciting to see people
thinking about the different design approaches :-)

I think we have sufficiently filled the box of ideas for the simple
example...I shall take everyon'e comments and suggestions from the analogue
and return to the true problem with renewed gusto, hehe :-)

Many, many thanks,
Lucy x

"Victor Bazarov" <v.********@comAcast.net> wrote in message
news:xqOtb.216244$Fm2.208804@attbi_s04...
"Gianni Mariani" <gi*******@mariani.ws> wrote...
Patchwork wrote:
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can help me with. It is related to my previous post but since it was pointed
out
that I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this
as
a spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to solve, but I have reduced it to as simple a concept as I can.


In cases like this, I have created a base class that has a method for
all different types of food.
[...]


The problem, I think, is that if somebody creates another class
descendent from CFood, such base class will not be able to process
it without having to change (at least a little bit).

Given certain limitations (all foods are known at the beginning,
all life forms are known at the beginning), there is no problem,
just make the base class for all life forms process all possible
foods (with default behaviour to reject it) and let every life
form override only those members that accept those (using double
dispatch because the "pantry" returns only a pointer to the base
food class). However, this mechanism is not very well suited
for expansion if needed. Adding foods requires rewriting the
base class for the life forms.

Rewriting the base class for life forms is not such a big deal,
of course. If it's the implementation we're talking about, the
derived classes won't have to change. However, from the design
point of view, it seems that it would be beneficial if some life
forms would be able to accept the new food and derive nutrition
from it simply because it _may_ be nutritious. That's why my
suggestion was to supply some kind of nutrition and taste info
in each food and let creatures decide whether each food is OK
to consume and how much nutrition and pleasure (or danger) to
derive from each food... Makes for more interesting process,
I believe :-)

Victor

Jul 19 '05 #7

P: n/a
Victor Bazarov wrote:
"Gianni Mariani" <gi*******@mariani.ws> wrote...
Patchwork wrote:
Hi Everyone,

I have a design related question (in C++) that I am hoping someone can
help
me with. It is related to my previous post but since it was pointed out
that
I was more or less asking the wrong questions about the wrong 'topic'
(polymorphism) I have posted this new question. Please don't see this as
a
spurious attempt to repost :-)

As mentioned previously, there is a very real problem I am trying to
solve,
but I have reduced it to as simple a concept as I can.
In cases like this, I have created a base class that has a method for
all different types of food.
[...]

The problem, I think, is that if somebody creates another class
descendent from CFood, such base class will not be able to process
it without having to change (at least a little bit).


That's right. The "little bit" is the key. You certainly don't want to
have to rewrite large chunks of code because a new food item was added.

Given certain limitations (all foods are known at the beginning,
all life forms are known at the beginning), there is no problem,
just make the base class for all life forms process all possible
foods (with default behaviour to reject it) and let every life
form override only those members that accept those (using double
dispatch because the "pantry" returns only a pointer to the base
food class). However, this mechanism is not very well suited
for expansion if needed. Adding foods requires rewriting the
base class for the life forms.
In the case I gave above, only the food types were hard coded. You
could have created any number of life-forms ( or food processors ) and
compiled them as a plug-in and all would work fine.

Rewriting the base class for life forms is not such a big deal,
of course. If it's the implementation we're talking about, the
derived classes won't have to change. However, from the design
point of view, it seems that it would be beneficial if some life
forms would be able to accept the new food and derive nutrition
from it simply because it _may_ be nutritious. That's why my
suggestion was to supply some kind of nutrition and taste info
in each food and let creatures decide whether each food is OK
to consume and how much nutrition and pleasure (or danger) to
derive from each food... Makes for more interesting process,
I believe :-)


This is where we get into the classic area of how much can you do
without explicit code.

The generic model is to have a generic N-key factory of processing methods.

class CFoodType;
class CProcessorType;

class CProcessor
{
virtual CProcessorType * Type() = 0;
};

class CFood
{
virtual CFoodType * Type() = 0;
};
class CHuman : public CProcessor
{
....

bool Eat( CFood * food )
{
Eater * eater =
Factory<Eater>::Find( Type(), food->Type() )->Create( this );

eater->Eat( food );

....

The question becomes - what happens if someone adds a new food and
someone else adds a new Processor, the intersection is likely broken.
There is usually no sensible generic default thing to do - it depends on
what it is you're trying to do and some problem sets there are multiple
possible outcomes.

For example, code-set conversion. There are converters that convert to
and from SJIS and EUC-JP - however there is a way to convert from
ISO-10646 to and from EUC-JP and SJIS, so ISO-2022-JP comes along, which
conversion do you use ? In this case you can make rules so that the
right thing happens and that conversions can be made for code-sets that
do not yet exist. In general, the multi-dimensional solution is problem
space dependant and a mechanism for selecting the Right (TM) object is
generally required.


Jul 19 '05 #8

P: n/a
You might want to have a design pattern where food is categorized as being
an organic substance with various properties such as flavor and texture.
Then, the matter of which food is desirable/edible to which lifeforms
becomes simply an overridable virtual function analyzing these properties.
Also, you can derive lifeforms from the base OrganicSubstance class, so now
lifeforms can eat each other if they want.

This simply mirrors what happens in reality, actually.
class CFlavor
{
public:
float m_fBitter;
float m_fSalty;
float m_fSweet;
}

class CTexture
{
public:
float m_fJuicy;
float m_fHard;
float m_fRough;
float m_fDoughy;
float m_fCrunchy;
// ... etc.
}

// The ctors for the above would assign some default bland
// flavor and texture; e.g. putting everything to 0.5.
class COrganic
{
public:
CFlavor m_flavor;
CTexture m_texture;

};
class CLifeForm : public COrganic
{
};

class CFood : public COrganic
{
};
Ray
Jul 19 '05 #9

P: n/a

class CFood : public COrganic
{
};

Sorry, there should be no CFood class; my bad.

Food is actually not really a type because the concept of food depends on
interpretation by consumers. There are organic entities, but whether they
are food or not depends on one's point of view, hence the flavor/texture
properties of organic entites.

Ray
Jul 19 '05 #10

This discussion thread is closed

Replies have been disabled for this discussion.