473,588 Members | 2,478 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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 1964
* 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(co nst
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::VEGE TABLE' (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********@warp mail.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(co nst
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::VEGE TABLE' (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 IsSuitableForVe getarian to the Food
class and then either swallow food or spit it out depending on the
specific implementation?

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

Can you add an abstract methond IsSuitableForVe getarian 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********@warp mail.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(co nst 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::VEGE TABLE' (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

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

50
6331
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 # this is OK attr2= # this is wrong def foo(self, data): self.attr1=data self.attr2.append(data) The initialization of attr1 is obviously OK, all instances of Father redefine it in the method foo. But the initialization of attr2 is wrong
13
2360
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 called CMemory and CMPU. They are the sub-classes. Two sub-classes have the relationship to communicate back and forth through this pointer. The pointer is responsible inside Top class for allocating and deallocating two sub-classes. CMemory class is responsible to allocate and deallocate memory...
15
9048
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 decision. This really is a toy project written for the purpose of learning to work with C++. It therefore makes some sense for me to give the situation the amount of consideration presented below. To be quite honest, I'm amazed at the amount there is to say about such a seemingly simple...
3
1808
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 pertaining to network and usb that inherit from an abstract parent with four basic functions: Open, Read, Write, and Close. // Note alot of the stuff has been left out // just to give a basic picture class IO { public:
1
2263
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, my_enum_2=NONE> class A { public: A(); virtual ~A() ;
11
2226
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 objects will simply have get and set methods, Struct is may be better...but i am looking for some advise from the experts on this? Assumptions: it is possible for some operations, i may have 8000 to 10000 (or more) objects instantiated. what are some considerations in designing a system that...
6
2109
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 should that dataset live? What client is responsible for instantiating the orders class? Would it be the ui layer or the master class in the business layer? thanks,
43
2057
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 less robust than calling the message loop functions within main? (It just doesn't "feel" right to me). Example 1: class MyProg { public:
6
2129
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 need some new features just use inhereitance. Often times when I program, I will create objects for a specific purpose for a program and if I need to add to it I just add the code.
29
2208
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 blank contract, fill in the data and then save a "new" contract. I have a method in my contract class called "Save" which is called like this... dim oContract as new Contract
0
8222
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
7984
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
8223
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
6634
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
5726
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
3847
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
2371
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
1458
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
1195
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.