469,946 Members | 2,002 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,946 developers. It's quick & easy.

class design question

This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a method
eat defined thus (* denotes the method eat)
human *----void (eat& food)
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group
thanks much and have a nice weekend.

GrahamO
Jul 23 '05 #1
31 1726
* grahamo:

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg

Also I have a base class human, and all humans eat so there's a method
eat defined thus (* denotes the method eat)

human *----void (eat& food)
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group


There is no best answer.

If you want a vegetarian to spit out any meat it's served, I suggest you check
the dynamic type of food in the vegetarian's override of 'eat'.

If you want a vegetarian to never be offered meat as food in the first place,
I suggest you remove the general 'eat' function, or make it non-public.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #2
The class hierarchies presented here violates the Liskov Substitution
Principle. The principle states:

In class hierarchies, it should be possible to treat a specialized
object
as if it were a base class object.

The following article describes this in detail:
http://www.eventhelix.com/RealtimeMa..._principle.htm

Deepa
--
EventStudio 2.5 - http://www.EventHelix.com/EventStudio
Enter model in plain text;generate sequence diagram in PDF/Word

Jul 23 '05 #3
* EventHelix.com:

The class hierarchies presented here violates the Liskov Substitution
Principle.


Actually no. Much is left unspecified. With some sets of more rigorous
specifications you get LSP violations, with others not.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #4

grahamo wrote:
This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a method eat defined thus (* denotes the method eat)
human *----void (eat& food) [ITYM 'void eat(food& f)'
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group


The answer is simple. You have a component 'foodtypes', which defines
an enumeration class 'FoodTypes' containing an enumeration, 'FoodType',
of all food types (e.g., MEAT, VEGETABLE, etc). You have another
component, 'fooutil', which defines a set of free functions in the
namespace 'FoodUtil'. One of these is the function 'isVegetable(const
food& f)', returning 'bool'. The 'food' protocol must define a const
virtual function 'identify()' returning 'int'. The 'vegetable' concrete
class implements 'identify' by returning 'FoodType::VEGETABLE' (as an
*int*). 'isVegetarian' is implemented by calling 'identify' on its
argument and switching on the result, which is interpreted as a
'FoodType'. The 'vegetarian' concrete class implements 'eat' by passing
its 'food' argument, 'f' (corrected in the picture above), to
'isVegetable'. If 'f' is a vegetable, 'eat' succeeds.
(Of course the problem is broken because 'eat' has to return a status
in order to know whether the 'vegetarian' ate the 'food'.)

Now, look at the component dependencies:

human key: a o--- b [a uses b in the interface]
o * a *--- b [a uses b in the implementation]
/ \
/ \
food foodutil
* *
\ /
\ /
foodtypes

'foodtypes' is likely to change as new foods are added. However, this
change is only visible to the 'foodutil' implementation (which must
recompile when the 'foodtypes' interface changes). The 'food' protocol
never changes. Nor does the 'human' protocol. 'foodutil' changes slowly
as most 'food's inherit their implementation of 'identify' from 'meat'
or 'vegetable', so most derived 'food's never recompile when
'foodtypes' is changed. This means that as new foods are added, the
application only has to relink, but only rarely recompile.

HTH, /david

Jul 23 '05 #5
da********@warpmail.net wrote:

grahamo wrote:
This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a

method
eat defined thus (* denotes the method eat)
human *----void (eat& food) [ITYM 'void eat(food& f)'
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group


The answer is simple. You have a component 'foodtypes', which defines
an enumeration class 'FoodTypes' containing an enumeration, 'FoodType',
of all food types (e.g., MEAT, VEGETABLE, etc). You have another
component, 'fooutil', which defines a set of free functions in the
namespace 'FoodUtil'. One of these is the function 'isVegetable(const
food& f)', returning 'bool'. The 'food' protocol must define a const
virtual function 'identify()' returning 'int'. The 'vegetable' concrete
class implements 'identify' by returning 'FoodType::VEGETABLE' (as an
*int*). 'isVegetarian' is implemented by calling 'identify' on its
argument and switching on the result, which is interpreted as a
'FoodType'. The 'vegetarian' concrete class implements 'eat' by passing
its 'food' argument, 'f' (corrected in the picture above), to
'isVegetable'. If 'f' is a vegetable, 'eat' succeeds.
(Of course the problem is broken because 'eat' has to return a status
in order to know whether the 'vegetarian' ate the 'food'.)


Wow. That's mighty complicated for avoiding a dynamic_cast.
But in principle you are doing just that: figure out if
the passed food object is of type vegetable.

Much simpler to do:

void Vegetarian::eat( food& meal )
{
if( dynamic_cast< Vegetable* >( &meal ) ) {
// proceed, meal is a vegetable or derived from
// it
}
}

Never duplicate information the compiler can provide you with!
That also means that having some sort of 'Identify Tag' in a class
isn't a very good idea. The compiler already provides you with one.
Just use that, instead of coming up with your own.

But the interesting point is: Can you do the very same thing
without a dynamic_cast?
Well. A possible solution would be to implement some form
of "double dispatch" which resolves to a function returning
true or false for the combination of person-type and food-type.
--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #6
Can you add an abstract methond IsSuitableForVegetarian to the Food
class and then either swallow food or spit it out depending on the
specific implementation?

Jul 23 '05 #7
"yv*********@gmail.com" wrote:

Can you add an abstract methond IsSuitableForVegetarian to the Food
class and then either swallow food or spit it out depending on the
specific implementation?


Sure you could.
But the question is: Do you want to do this?

The problem is, that now you have some knowledge of Vegetarians
in the Food class and usually this is not a good idea. An abstract
Food class should not have some knowledge that there are special
classes derived from it. (That is eg. one of the problems with
the well known 'double-dispatch' method in C++ which uses a virtual
function just to dispatch a second virtual call based on the this pointer:

#include <iostream>

class B;
class C;

class A
{
public:
virtual void Use( A& X ) = 0;
virtual void Use( B& B ) = 0;
virtual void Use( C& C ) = 0;
};

class B: public A
{
public:
virtual void Use( A& X ) { X.Use( *this ); }
virtual void Use( B& B ) { std::cout << "B - B\n"; }
virtual void Use( C& C ) { std::cout << "B - C\n"; }
};

class C: public A
{
public:
virtual void Use( A& X ) { X.Use( *this ); }
virtual void Use( B& B ) { std::cout << "C - B\n"; }
virtual void Use( C& C ) { std::cout << "C - C\n"; }
};
void foo( A& Obj1, A& Obj2 )
{
// call a function based on the
// runtime type of Obj1 *and* Obj2

Obj1.Use( Obj2 );
}

int main()
{
B MyB;
C MyC;

foo( MyB, MyB );
foo( MyC, MyC );
foo( MyB, MyC );
foo( MyC, MyB );
}

The problem is, that class A needs to have knowledge that there is a class B and
a class C derived from it. Whenever you need to introduce a new class in that
hierarchy, you must not forget to update class A with a new virtual function and
all classes derived vom A with functions handling that. If you don't do it, you
create a nice never ending recursion.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #8

Karl Heinz Buchegger wrote:
da********@warpmail.net wrote:

grahamo wrote:
This came up in an interview I did a while ago and I wanted to know the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg" that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a method
eat defined thus (* denotes the method eat)
human *----void (eat& food) [ITYM 'void eat(food& f)'
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will never eat meat, in terms of our class design etc. There's probably a million and one answers but I'd like to get the best one from the
experts on this group


The answer is simple. You have a component 'foodtypes', which defines an enumeration class 'FoodTypes' containing an enumeration, 'FoodType', of all food types (e.g., MEAT, VEGETABLE, etc). You have another
component, 'fooutil', which defines a set of free functions in the
namespace 'FoodUtil'. One of these is the function 'isVegetable(const food& f)', returning 'bool'. The 'food' protocol must define a const virtual function 'identify()' returning 'int'. The 'vegetable' concrete class implements 'identify' by returning 'FoodType::VEGETABLE' (as an *int*). 'isVegetarian' is implemented by calling 'identify' on its
argument and switching on the result, which is interpreted as a
'FoodType'. The 'vegetarian' concrete class implements 'eat' by passing its 'food' argument, 'f' (corrected in the picture above), to
'isVegetable'. If 'f' is a vegetable, 'eat' succeeds.
(Of course the problem is broken because 'eat' has to return a status in order to know whether the 'vegetarian' ate the 'food'.)


Wow. That's mighty complicated for avoiding a dynamic_cast.
But in principle you are doing just that: figure out if
the passed food object is of type vegetable.

Much simpler to do:

void Vegetarian::eat( food& meal )
{
if( dynamic_cast< Vegetable* >( &meal ) ) {
// proceed, meal is a vegetable or derived from
// it
}
}


Well, if you are going to use dynamic_cast, there is really no point in
having polymorphism.
Never duplicate information the compiler can provide you with!
That also means that having some sort of 'Identify Tag' in a class
isn't a very good idea. The compiler already provides you with one.
Just use that, instead of coming up with your own.
Actually, there is no identifier tag. That is the whole point. You have
a virtual function that returns opaque data. The trick is to use the
utility to get meaningful semantics. For example, 'vegetarian' can use
a whole suite of utility functions like 'isVegetable', 'isOvaLacto',
'isSaltFree' etc. In order for you to get the same behavior, your
'vegetarian' class becomes dependent on the entire 'food' class
hierarchy because you have to do a dynamic_cast to every 'food'
subtype. That's wrong.
But the interesting point is: Can you do the very same thing
without a dynamic_cast?
I gave one.
Well. A possible solution would be to implement some form
of "double dispatch" which resolves to a function returning
true or false for the combination of person-type and food-type.


That's what it does. /david

Jul 23 '05 #9
EventHelix.com wrote:
The class hierarchies presented here violates the Liskov Substitution
Principle. The principle states:

In class hierarchies, it should be possible to treat a specialized
object
as if it were a base class object.

Why it violates it?

The following article describes this in detail:
http://www.eventhelix.com/RealtimeMa..._principle.htm

I guess the LSP must be followed only where it makes sense to do so, I
do not think such silver-bullet rules can exist.

--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #10
grahamo wrote:
This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a method
eat defined thus (* denotes the method eat)
human *----void (eat& food)
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group
thanks much and have a nice weekend.

GrahamO

As it has been mentioned in the thread you can either use an enumeration
for food and do:
enum food { meat=1, veg };
// Abstract if you need it to be abstract
class human
{
// ...

public:

// virtual if you need it to be virtual, other wise non-virtual
virtual void eat (food &f)=0;
virtual ~human() {}
// ...
};
class vegetarian: public human
{
// ...

public:

virtual void eat(food &f)
{
if(f==veg)
{
// process food
}

else
// do something
}

// ...
}
// ...
};

or use RTTI with dynamic_cast (perhaps in a pointer conversion, but
since you are using reference here, I will continue using references):
#include <typeinfo>
class food
{
// ...

public:
virtual ~food() {}
};
class veg: public food
{
// ...
public:
//...
};

class vegetarian: public human
{
// ...

public:

virtual void eat(food &f) try
{

veg &rveg= dynamic_cast<veg &>(f);

// Process food
}

catch(std::bad_cast)
{
// Do something
}

// ...
};


--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #11

Ioannis Vranos wrote:
grahamo wrote:
This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg

[snip] As it has been mentioned in the thread you can either use an enumeration for food and do:
enum food { meat=1, veg };
Not quite like this. The problem statement specifies that 'food' is a
class.
or use RTTI with dynamic_cast (perhaps in a pointer conversion, but
since you are using reference here, I will continue using

references):

Again, this solution does not scale. /david

Jul 23 '05 #12
da********@warpmail.net wrote:

Karl Heinz Buchegger wrote:
da********@warpmail.net wrote:

grahamo wrote:
> This came up in an interview I did a while ago and I wanted to know > the correct answer. The setup is this;
>
> If I have a base class "food" and also two classes "meat" and "veg" > that inherit from food, thus;
>
> food
> / \
> / \
> meat veg
>
>
> Also I have a base class human, and all humans eat so there's a
method
> eat defined thus (* denotes the method eat)
>
>
> human *----void (eat& food) [ITYM 'void eat(food& f)'
> / \
> / \
> vegetarian carnivore
>
>
>
> Now the question is how do we work things such that a vegaterian will > never eat meat, in terms of our class design etc. There's probably a > million and one answers but I'd like to get the best one from the
> experts on this group

The answer is simple. You have a component 'foodtypes', which defines an enumeration class 'FoodTypes' containing an enumeration, 'FoodType', of all food types (e.g., MEAT, VEGETABLE, etc). You have another
component, 'fooutil', which defines a set of free functions in the
namespace 'FoodUtil'. One of these is the function 'isVegetable(const food& f)', returning 'bool'. The 'food' protocol must define a const virtual function 'identify()' returning 'int'. The 'vegetable' concrete class implements 'identify' by returning 'FoodType::VEGETABLE' (as an *int*). 'isVegetarian' is implemented by calling 'identify' on its
argument and switching on the result, which is interpreted as a
'FoodType'. The 'vegetarian' concrete class implements 'eat' by passing its 'food' argument, 'f' (corrected in the picture above), to
'isVegetable'. If 'f' is a vegetable, 'eat' succeeds.
(Of course the problem is broken because 'eat' has to return a status in order to know whether the 'vegetarian' ate the 'food'.)

Wow. That's mighty complicated for avoiding a dynamic_cast.
But in principle you are doing just that: figure out if
the passed food object is of type vegetable.

Much simpler to do:

void Vegetarian::eat( food& meal )
{
if( dynamic_cast< Vegetable* >( &meal ) ) {
// proceed, meal is a vegetable or derived from
// it
}
}


Well, if you are going to use dynamic_cast, there is really no point in
having polymorphism.
Never duplicate information the compiler can provide you with!
That also means that having some sort of 'Identify Tag' in a class
isn't a very good idea. The compiler already provides you with one.
Just use that, instead of coming up with your own.


Actually, there is no identifier tag. That is the whole point.


At the moment you return some magical constant from some function
you have some sort of identifying tag.

But fell free to implement with your method:

class Food
{
...
};

class Vegetable : public Food
{
...
};

class Salad : public Vegetable
{
...
};

I really would like to see how you implement
IsVegetable() and
IsSalad()
with your method of using a virtual identify() method
in the base class.

Note: For a Salad object, both IsVegetable() and IsSalad()
have to return true.
a virtual function that returns opaque data. The trick is to use the
utility to get meaningful semantics. For example, 'vegetarian' can use
a whole suite of utility functions like 'isVegetable', 'isOvaLacto',
'isSaltFree' etc. In order for you to get the same behavior, your
'vegetarian' class becomes dependent on the entire 'food' class
hierarchy because you have to do a dynamic_cast to every 'food'
subtype.


But at least it solves the above problem. A Salad object *is* a Vegetable
object, thus the dynamic_cast succeeds.
Yes. The Vegetarian class gets dependent on the food class. And yes, I agree,
usually it is not a good idea. That's why using such identify tags (be it some magical
return value or using a dynamic_cast) should be used wisely. But if those
identify tags are the solution, then at least don't do it by introducing some
magical constants and a function which returns one of them.

Well. A possible solution would be to implement some form
of "double dispatch" which resolves to a function returning
true or false for the combination of person-type and food-type.


That's what it does. /david


Sorry. But a double dispatch is something completely different.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #13
Ioannis Vranos wrote:

virtual void eat(food &f) try
{

veg &rveg= dynamic_cast<veg &>(f);

// Process food


There is a potential problem with that.
dynamic_cast throws an exception if you try to cast
a reference and the cast does not succeed. So in this
case casting a pointer (where no exception is thrown)
is really better:

veg* pVeg = dynamic_cast< veg* >( &f );

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #14
<The problem is, that now you have some knowledge of Vegetarians
<in the Food class and usually this is not a good idea.

having done some grocery shopping I know for a fact that there is a lot
of food packages (labeled "low fat", "low carb" and alike) out there
targeted for specific food consumers - a clear example as it seems to
me that in the real world the class Food knows a lot about the specific
subclasses of the class Human :)

Jul 23 '05 #15
"yv*********@gmail.com" wrote:

<The problem is, that now you have some knowledge of Vegetarians
<in the Food class and usually this is not a good idea.

having done some grocery shopping I know for a fact that there is a lot
of food packages (labeled "low fat", "low carb" and alike) out there
targeted for specific food consumers - a clear example as it seems to
me that in the real world the class Food knows a lot about the specific
subclasses of the class Human :)


:-)

One could it express differently:
The consumer reading the words, knows what "low fat" means to him.
Different consumers give different meaning to those words. So it
is not the food that knows something about itself, but it is the
consumer who knows how to interpret the hints given by the food.

And of course, sometimes the consumer has to ask the clerk.

Which brings us to a 3-rd way of implementing the problem: Install
some Clerk class, who decides if a food can be handled by a customer.
Eg. It does this by asking both classes for their properties and tries
to match them.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #16
yv*********@gmail.com wrote:
<The problem is, that now you have some knowledge of Vegetarians
<in the Food class and usually this is not a good idea.

having done some grocery shopping I know for a fact that there is a lot
of food packages (labeled "low fat", "low carb" and alike) out there
targeted for specific food consumers - a clear example as it seems to
me that in the real world the class Food knows a lot about the specific
subclasses of the class Human :)

Actually the above means that Human knows what food types are suitable
for him.

--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #17
da********@warpmail.net wrote:
Again, this solution does not scale. /david

Why?

--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #18
Karl Heinz Buchegger wrote:
There is a potential problem with that.
dynamic_cast throws an exception if you try to cast
a reference and the cast does not succeed. So in this
case casting a pointer (where no exception is thrown)
is really better:

veg* pVeg = dynamic_cast< veg* >( &f );

In the code it was pointed out that pointers could be used, and there is
a catch() block for the exception.


--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #19
Change the abstract methond in Food from IsSuitableForVegetarian to
ContainsAnimalProtein (or whatever else the vegetarians don't eat) and
you don't need a clerk, an educated consumer can take it from there and
decide what to do with this food.

Jul 23 '05 #20
grahamo wrote:
This came up in an interview I did a while ago and I wanted to know
the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat" and "veg"
that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so there's a method
eat defined thus (* denotes the method eat)
human *----void (eat& food)
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group
At first sight, inheritance might seem a suitable tool for this problem.

struct Carnivore
{
virtual void eat(Meat&)=0;
};

struct Herbivore
{
virtual void eat(Herbs&)=0;
};

struct Omnivore : public Carnivore, public Herbivore
{
};

Great! Omnivores eat both types of food.

But, what if we had a composite food containing both Meat and Herbs?

struct Pie : public Meat, public Herbs
{
};

An omnivore could eat a pie (but it would have to be cast as Meat or
Herbs), but you can feed a Pie to a Herbivore, which is perhaps not the
intended behaviour.

One could have quite complicated rules about what can eat what, and one
needs to be able to deal with exceptions (e.g. I am an omnivore, but I
don't eat tomatoes, nuts or monkey). I don't think inheritance can
represent such rules.

So you might need to take a step back and just write

struct Eater
{
virtual void eat(Food&)=0;
};

and derive class hierarchies from Eater and Food. Eater will check the
food using dynamic_casts, and throw exceptions (or perform other
actions) depending on the food type. It's not so pretty but it is
flexible and will work.

thanks much and have a nice weekend.

GrahamO

Jul 23 '05 #21
Now the question is how do we work things such that a vegaterian will
never eat meat, in terms of our class design etc. There's probably a
million and one answers but I'd like to get the best one from the
experts on this group


I would say it is an incomplete class design or at least a poor scheme...

However, to better model/mimic real life, humans eat food, period. So I
would have an eat method on the human class that takes food. I'd
specialize human into vegitarian, carnivore (are there any?) and
omnivore. A vegitarian would just have to deal with being "offered"
meat to eat. It could possibly reject it. You could also model the
real world by giving humans a method to "advertise" their preference.
That way some entity preparing food for a human could query it so as to
find out if it were vegitarian or not.
Jul 23 '05 #22
> This came up in an interview I did a while ago and I
wanted to know the correct answer. The setup is this;

If I have a base class "food" and also two classes "meat"
and "veg" that inherit from food, thus;

food
/ \
/ \
meat veg
Also I have a base class human, and all humans eat so
there's a method eat defined thus (* denotes the method
eat)
human *----void (eat& food)
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a
vegaterian will never eat meat, in terms of our class
design etc. There's probably a million and one answers
but I'd like to get the best one from the experts on this
group
thanks much and have a nice weekend.

GrahamO


#include<iostream>
#include<iterator>
#include<string>
#include<vector>

using std::cout;
using std::string;
using std::vector;
using std::iterator;

struct Food{virtual string what()=0;};
struct Meat:Food{string what(){return"Saucage";}};
struct Weed:Food{string what(){return"Cabbage";}};
struct Human
{
vector<Food*>stomach;
virtual void eat(Food&a){stomach.push_back(&a);}
void puke()
{
for(vector<Food*>::iterator a=stomach.begin();a!=stomach.end();a++)
cout<<'\t'<<(*a)->what();
}
};
struct Carnivore:Human{};
struct Vegetarian:Human
{
void eat(Meat&){}
void eat(Weed&a){Human::eat(a);}
};
int main()
{
Carnivore meatball;
Vegetarian veggie;
Meat meat;
Weed weed;
meatball.eat(meat);
meatball.eat(weed);
veggie.eat(meat);
veggie.eat(weed);
cout<<"\nMeatball had: ";
meatball.puke();
cout<<"\nVeggie had: ";
veggie.puke();
return 0;
}
Jul 23 '05 #23
The way Graham0 specified the problem, it does seem to violate LSP.

Both the class hierarchies fail on the substitution test.

Deepa
--
EventStudio 2.5 - http://www.EventHelix.com/EventStudio
Enter model in plain text;generate sequence diagram in PDF/Word

Jul 23 '05 #24
* EventHelix.com:

The way Graham0 specified the problem, it does seem to violate LSP.

Both the class hierarchies fail on the substitution test.


Nope.

True: in natural language a "carnivore" is not necessarily a "human".

False: a "carnivore" in the class hierarchy is necessarily something more than
a class derived from the class "human".

Do not expect a C++ "display" class to necessarily have any of the properties
of a physical display.

Do not expect a C++ "human" class to necessarily have any of the properties
of a physical human. :-)

Hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #25

Phil Staite wrote:
Now the question is how do we work things such that a vegaterian will never eat meat, in terms of our class design etc. There's probably a million and one answers but I'd like to get the best one from the
experts on this group
I would say it is an incomplete class design or at least a poor

scheme...

This is apparantly a very good observation.

After consulting with some collegues, I learned a very nice solution to
this problem. Look at the problem again:

[slightly modified] food
/ \
/ \
meat vegetable

Also I have a base class human, and all humans eat so there's a
method eat defined thus (* denotes the method eat)

human [void eat(food&)]
/ \
/ \
vegetarian carnivore

Now the question is how do we work things such that a vegaterian
will never eat meat, in terms of our class design etc.


The problem is that you have a method 'eat' in 'human' which cannot be
satisfied by all subtypes. Since 'eat' returns 'void' it is a no-fail
operation. However, 'vegetarian's cannot 'eat' if 'food' is 'meat'.
Therefore, 'vegetarian' is a non-meat-eating human. The way you solve
this is a two-step approach. First, you break the class hierarchy to
indicate that vegetarians only eat 'vegetable's:

vegetarian carnivore
| |
| |
| V
| human
| [void eat(food&)]
|
V
non-meat-eating-human
[void eat(vegetable&)]

Then, you recombine the protocols, with a name-change, into a protocol
hierarchy:

vegetarian carnivore
| |
| |
| V
| meat-eating-human
| [void eatAnything(food&)]
| /
| /
| /
V \/_
human
[void eat(vegetable&)]

This hierarchy enforces the rule that while all 'humans' eat vegetables
('vegetarian's in particular), only 'meat-eating-humans' 'eat' meat in
addition to 'vegetable's. Now, 'eat' is a no-fail operation that can be
satisfied by all subtype. (Of course, the subtype of
'meat-eating-human' is called 'carnivore', which suggests that
instances of this class only eat meat, but it is easy to see how the
transformation can be extended to include omnivores, and specialize
'carnivore').

/david

Jul 23 '05 #26

Martijn Mulder wrote:
veggie.eat(weed);


I've never heard of anyone *eating* weed...

/david

Jul 23 '05 #27
>> veggie.eat(weed);
I've never heard of anyone *eating* weed...


How about smoked bacon?
Jul 23 '05 #28

Ioannis Vranos wrote:
da********@warpmail.net wrote:
Again, this solution does not scale. /david

Why?


Every time you want to test some aspect of 'food', you have to
dynamic_cast the instance to some new 'food' subtype (e.g., is the food
sugar-free AND fat-free AND salt-free, etc). This tightly couples the
two class hierarchies because various 'human' subtypes have to know
about many 'food' subtypes. The first solution I gave moves all these
dependencies into one component, which is a bit better. However, the
whole problem can be refactored (as I show else-thread) to enforce more
rules at compile-time. /david

Jul 23 '05 #29
da********@warpmail.net wrote:
Martijn Mulder wrote:

veggie.eat(weed);

I've never heard of anyone *eating* weed...

/david

Isn't that a magic brownie? ;)
Jul 23 '05 #30
da********@warpmail.net wrote:
Every time you want to test some aspect of 'food', you have to
dynamic_cast the instance to some new 'food' subtype (e.g., is the food
sugar-free AND fat-free AND salt-free, etc). This tightly couples the
two class hierarchies because various 'human' subtypes have to know
about many 'food' subtypes. The first solution I gave moves all these
dependencies into one component, which is a bit better. However, the
whole problem can be refactored (as I show else-thread) to enforce more
rules at compile-time. /david

I think the sub-types that you mentioned are not real sub-types but
additional "properties" of food, and these details depend on what
situation the OP is modelling (for example a restaurant?).

--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #31
Ioannis Vranos wrote:

Karl Heinz Buchegger wrote:
There is a potential problem with that.
dynamic_cast throws an exception if you try to cast
a reference and the cast does not succeed. So in this
case casting a pointer (where no exception is thrown)
is really better:

veg* pVeg = dynamic_cast< veg* >( &f );


In the code it was pointed out that pointers could be used, and there is
a catch() block for the exception.


My bad.
I missed it completely.
--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #32

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

50 posts views Thread by Dan Perl | last post: by
13 posts views Thread by Bryan Parkoff | last post: by
15 posts views Thread by Steven T. Hatton | last post: by
3 posts views Thread by fernandez.dan | last post: by
11 posts views Thread by dimension | last post: by
6 posts views Thread by rodchar | last post: by
43 posts views Thread by Tony | last post: by
6 posts views Thread by JoeC | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.