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 31 1838
* 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?
* 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?
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 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
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?
"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
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
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
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
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 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
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
<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 :)
"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 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
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
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.
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 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.
> 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;
}
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
* 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?
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
Martijn Mulder wrote: veggie.eat(weed);
I've never heard of anyone *eating* weed...
/david
>> veggie.eat(weed); I've never heard of anyone *eating* weed...
How about smoked bacon?
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 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? ;) 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
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 This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Dan Perl |
last post by:
There is something with initializing mutable class attributes that I am
struggling with. I'll use an example to explain:
class Father:
attr1=None...
|
by: Bryan Parkoff |
last post by:
I have created three classes according to my own design. First class
is called CMain. It is the Top Class. Second class and third class are...
|
by: Steven T. Hatton |
last post by:
The following may strike many of you as just plain silly, but it represents
the kind of delelima I find myself in when trying to make a design...
|
by: fernandez.dan |
last post by:
I'm still learning how to use Object Oriented concepts. I'm have a
basic question dealing with design. I have two classes that deal with
I/O...
|
by: Alfonso Morra |
last post by:
if I have a class template declared as ff:
(BTW is this a partial specialization? - I think it is)
template <typename T1, myenum_1 e1=OK,...
|
by: dimension |
last post by:
If my intentions are to create objects that encapsulates data rows in a
table, is it better to use a Struct or Class? Based on what i read, if my...
|
by: rodchar |
last post by:
Hey all,
I'm trying to understand Master/Detail concepts in VB.NET. If I do a data
adapter fill for both customer and orders from Northwind where...
|
by: Tony |
last post by:
I'm working with GUI messaging and note that MFC encapsulates
the message loop inside of a C++ class member function. Is this
somehow inherently...
|
by: JoeC |
last post by:
I have a question about designing objects and programming. What is the
best way to design objects? Create objects debug them and later if you...
|
by: Brad Pears |
last post by:
Here is a simple OO design question...
I have a Contract class. The user can either save an existing contract or
they start off fresh with a...
|
by: tammygombez |
last post by:
Hey fellow JavaFX developers,
I'm currently working on a project that involves using a ComboBox in JavaFX, and I've run into a bit of an issue....
|
by: tammygombez |
last post by:
Hey everyone!
I've been researching gaming laptops lately, and I must say, they can get pretty expensive. However, I've come across some great...
|
by: better678 |
last post by:
Question:
Discuss your understanding of the Java platform. Is the statement "Java is interpreted" correct?
Answer:
Java is an object-oriented...
|
by: teenabhardwaj |
last post by:
How would one discover a valid source for learning news, comfort, and help for engineering designs? Covering through piles of books takes a lot of...
|
by: CD Tom |
last post by:
This only shows up in access runtime. When a user select a report from my report menu when they close the report they get a menu I've called Add-ins...
|
by: Naresh1 |
last post by:
What is WebLogic Admin Training?
WebLogic Admin Training is a specialized program designed to equip individuals with the skills and knowledge...
|
by: antdb |
last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine
In the overall architecture, a new "hyper-convergence" concept was...
|
by: Matthew3360 |
last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function.
Here is my code.
...
|
by: Matthew3360 |
last post by:
Hi, I have a python app that i want to be able to get variables from a php page on my webserver. My python app is on my computer. How would I make it...
| |