Im new to OOP concepts (Abstraction, Encapsulation, Inheritance and Polymorphism).
Could someone please explain OOP using a SPORTS example such as Sport which can have subclasses (Football, Rugby, Cricket, Boxing, snooker, etc).
Also what would be the basic structure for a Athlete Management System.
Any help would be greatly appreciated.
Many thanks.
This is a pretty generic question, and not easy to answer precisely, but here are a few pointers, which should hopefully give you some ideas about Inheritance and Polymorphism at least.
All sports (the concepts) are in fact, instances of Sport (the class). This means that the classes Football, Boxing, etc, all inherit from the class Sport.
Things that can be done in Sport, can be done in any subclass of Sport, since it is in fact, a sport. For instance, if Cheer(), Score(), and win() are defined as methods in Sport, then each of the sub-classes can also perform these - that is inheritance!
Each sport may however, have different ways of scoring. If so, they can override the method from Sport, and provide their own logic for this. They must, however, still comply with the definition of the method Score() set down in Sport, for them to be able to behave as a Sport. This is polymorphism!
Cheering and winning may be the same concept in all sports, so they can just be inherited from Sport
A simple pseudo code example. This may be pretty stupid (and maybe not even technically correct in sports terms, but I hope you get the point! :))
public class Sport{
public function score(){
print("Scored!");
}
public function Win(){
print("Won!");
}
public function Cheer(){
print("Cheeeeeeeeer!!!");
}
}
public class Soccer Inherits Sport{
public override function score(){
print("One goal!");
}
}
public class Boxing Inherits Sport{
public override function score(){
print("Knockout!");
}
}
// Using the classes:
Sport generalSport = new Sport(); // generalSport is just a non-specific sport
Sport soccerSport = new Soccer(); // soccerSport is a Soccer, but also a Sport!
Sport boxingSport = new Boxing(); // boxingSport is a Boxing, but also a Sport!
generalSport.Win(); // This should output: "Won!" (from Sport)
soccerSport.Win(); // ... "Cheeeeeeeeer!!!" (inherited from Sport)
boxingSport.Cheer() // ... "Cheeeeeeeeer!!!" (inherited from Sport)
generalSport.score(); // "Scored!"
soccerSport.score(); // "One Goal", since it was overridden in Soccer
boxingSport.score() // "Knockout!", since overridden in Boxing
Hint: Just think of what things "are" in real life. If X inherits Y, X may be defined as an X specifically, but it is still a Y too, and can be treated as such (only with the possibility of adding a few extra properties).
Related
I'm relatively new to this site so if I am doing something wrong when it comes to posting questions and whatnot please let me know so I can fix it for next time.
I'm curious as to whether or not it is bad OOP practice to subclass multiple classes from a single base class. That probably doesn't quite make sense so I'm going to elaborate a little bit.
Say for instance you are designing a game and you have several different monsters you might come across. The approach I would take is to have a base abstract class for just a general monster object and then subclass all of the specific types of monsters from the base class using a new class.
One of my instructors told me that you shouldn't rely on inheritance in this case because the number of monsters will continue to grow and grow and the number of classes will increase to a point where it is hard to keep track of all of them and thus yo will have to recompile the program with every new class added in the future.
I don't understand why (or even if) that's a bad thing. If anybody could help me understand where my professor is coming from that would be much appreciated.
Thanks!
If monsters are very similar, in that the only differences are (for example) their name, how much damage they impart, what color they are, etc., then these differences which can be reflected in a field (in values), may make sub-classing unnecessary.
If, however, you have monsters that are fundamentally different from others, such that it is necessary to have very different methods and logic, and more specifically, differences that cannot be reflected in fields, then a sub-class SpecialMonster may be necessary.
But again, even SpecialMonster may not need to be sub-classed by individual monster types, as it's fields may be enough to distinguish between them.
While it's legal to have a million sub-classes of specific monster types, you don't want to take care of all that duplicate code when it could simply be expressed in the fields of new Monster instances, such as
new Monster("Goob", WakeTime.NOCTURNAL, 35, new Weapon[]{sword, hammer, knittingNeedle});
new Monster("Mister Mxyzptlk", WakeTime.ANYTIME, 71, new Weapon[]{sword, mindMeld, cardboardCutter});
There is an alternative, where you do have a lot of classes, but you don't impose them onto your users, and you don't clutter up your API/JavaDoc with them. If your Monster happens to be an abstract class
public abstract class Monster {
private final String name;
...
public Monster(String name, int default_damage, WakeTime wake_time, Weapon[] weapons) {
this.name = name;
...
}
public String getName() {
return name;
}
...
public abstract int getDamage(int hit_strength);
}
Then you could have a Monster convenience creator like this:
/**
<P>Convenience functions for creating new monsters of a specific type.</P>
**/
public class NewMonsterOfType {
private NewMonsterOfType() {
throw new IllegalStateException("Do not instantiate.");
}
/**
<P>Creates a new monster that is nocturnal, has 35-default-damage, and whose weapens are: sword, hammer, knittingNeedle.</P>
**/
public static final GOOB = new GoobMonster();
/**
<P>Creates a new monster that can be awake at any time, has 71-default-damage, and whose weapens are: sword, mindMeld, cardboardCutter.</P>
**/
public static final MISTER_MXYZPTLK = new MisterMxyzptlkMonster();
}
class GoobMonster extends Monster {
public GoobMonster() {
super("Goob", WakeTime.NOCTURNAL, 35, new Weapon[]{sword, hammer, knittingNeedle});
}
public int getDamage(int hit_strength) {
return (hit_strength < 70) ? getDefaultDamage() : (getDefaultDamage() * 2);
}
}
class MisterMxyzptlkMonster extends Monster {
public GoobMonster() {
super("Mister Mxyzptlk", WakeTime.ANYTIME, 71, new Weapon[]{sword, mindMeld, cardboardCutter});
}
public int getDamage(int hit_strength) {
return (hit_strength < 160) ? getDefaultDamage() + 10 : (getDefaultDamage() * 3);
}
}
In order for these private (actually package-protected) classes to not show up in you JavaDoc, you need to set its access to something either protected or public.
Inheritance is quite natural in your scenario as all the specific monsters ARE base monsters as well :). I'd actually use inheritance a lot here, since probably specific monsters do have specific behaviour that would have to be overriden. MonsterA might move by crawling while MonsterB might move by flying. The base AMonster would have an abstract Move() method , implemented by those sub types.
This isn't a final answer, it really much depends on the game needs, however, in simplified form, inheritance makes sense here. The number of monster types might continue to grow, but really, are they all the same? The monster design is just based on grouping together some predefined data/behaviour? The game is quite trivial then...
I really get the impression your instructor doesn't code games for a living (me neither, although I did make a game some time ago), but his explanation why you shouldn't use inheritance is way too simplified. The number of defined classes is never an issue in an app, the more the better IF the Single Responsibility Principle is respected.
About you have to recompile your app.... yeah, when you fix a bug you have to recompile it too. IMO, the reasons he gave to you aren't valid for this scenario. He needs to come up with much better arguments.
In the mean time, go for inheritance.
Theoretical question needs theoretical answer :).
It is not just bad, it is pointless. You should have a LIMITED number of "base" classes that inherits from other classes, and those classes should be composed from other classes (vide favour composition versus inheritance).
So as complexity grows the number of classes that base classes are composed from should grows. Not number of base classes itself.
It is like in the industry. If you see machines for instance, they are really composed from large quantity of small parts, and some of those small parts are the same in different machines. When yo designing new machine you do not order new unique "base" part for it just to have a name for your new machine. You use parts existing on a market and you designing some new parts (not "base") only if you cannot find existing counterparts...
How to model a domain when you have a base class and 2 classes extending this base class and only one of derived class has a relationship with another object.
Example:
public abstract class Base
{
public abstract void method();
}
public class1 extends Base
{
public void method()
{
do some stuff
}
}
public class2 extends Base
{
private Class3 class3;
public void method()
{
do other stuff
}
public Class3 getClass3(){...}
public void setClass3(Class3 class3){...}
}
Is this model breaking Liskov principle? I think so because of this relation with class3, so we have to figure out how to model without this relation or to move this relation to Base. If I have a part of program that deal with Class2 to handle the relation whith Class3 I can't work with base class without cast to class2.
Is this thought right?
Clarifying...
Let's think in learning model. We have Courses and CourseClasses. We can also have a online courses and presencial courses. In presencial courses we may face with cost of this training. So costs only make sense to presencial environment. CourseClasses could have range dates or quatitative dates.
Today I have this model:
Course
{
...
}
public abstract class CourseClass
{
private Course course;
// getter and setter to course
public abstract Enrollment enroll(Person student);
}
public class QuantitativeCourseClass
{
public Enrollment enroll(Person student)
{
// enroll for quantitative
}
}
public class RangeCourseClass
{
public Enrollment enroll(Person student)
{
// enroll for range
}
}
Now I have to deal with costs and till this moment presencial course isn't important to me but now, cost only make sense to presencial enviroment.
My problem is: I need to deal with CourseClass object in cost module because I need some stuff of courseClass, but the relationship of cost is with RangeCourseClass because QuantitativeCourseClass don't make sense to prensecial environment.
The question about liskov is about how to convence my team to make some modifications in this model.
if class3 has nothing to do with base, then it should not be in the base. You can't "break" LSP, since the compiler enforces it. downcasting is not something that is preferred, but doing so doesn't break LSP.
The purpose of inheritence is to have an "is-a" relationship. A cat is-a(n) animal. A toyota is-a car.
What you're talking about is moving the toyota emblem to the car class just because you want to make things easier. That's not good design at all.
In short, it's worse design to move things to the base class than it is to downcast to the specific class.
I think you have mixed up the direction of LSP (Liskov Substitution Principle): LSP is (strong) behavioral subtyping, not strong behavioral supertyping. So LSP is not working against your example, but for your example:
Is this model breaking Liskov principle? I think so because of this
relation with class3, so we have to figure out how to model without
this relation or to move this relation to Base. If I have a part of
program that deal with Class2 to handle the relation with Class3 I
can't work with base class without cast to class2.
Your model is not breaking LSP. If you have a part of program that uses some variable var that deals specifically with Class2 (i.e. parts not present in Base), you need to declare var to be of Class2. So no downcast is necessary. And LSP guarantees that var behaves as Base, too, so no explicit upcast is necessary, either.
As i understand, you can not view the problem without knowing the problem aspects (geometry, for example). So, i can not understand meaning of your architecture. For example, the famous LSP violation Example:
Square:Rectangle -it looks fine, when it stand in "side". But, when you start use and you put some functions around, you can see the problem.
I had a discussion at work regarding "Inheritance in domain model is complicating developers life". I'm an OO programmer so I started to look for arguments that having inheritance in domain model will ease the developer life actually instead of having switches all over the place.
What I would like to see is this :
class Animal {
}
class Cat : Animal {
}
class Dog : Animal {
}
What the other colleague is saying is :
public enum AnimalType {
Unknown,
Cat,
Dog
}
public class Animal {
public AnimalType Type { get; set; }
}
How do I convince him (links are WELCOME ) that a class hierarchy would be better than having a enum property for this kind of situations?
Thanks!
Here is how I reason about it:
Only use inheritance if the role/type will never change.
e.g.
using inheritance for things like:
Fireman <- Employee <- Person is wrong.
as soon as Freddy the fireman changes job or becomes unemployed, you have to kill him and recreate a new object of the new type with all of the old relations attached to it.
So the naive solution to the above problem would be to give a JobTitle enum property to the person class.
This can be enough in some scenarios, e.g. if you don't need very complex behaviors associated with the role/type.
The more correct way would be to give the person class a list of roles.
Each role represents e.g an employment with a time span.
e.g.
freddy.Roles.Add(new Employement( employmentDate, jobTitle ));
or if that is overkill:
freddy.CurrentEmployment = new Employement( employmentDate, jobTitle );
This way , Freddy can become a developer w/o we having to kill him first.
However, all my ramblings still haven't answered if you should use an enum or type hierarchy for the jobtitle.
In pure in mem OO I'd say that it's more correct to use inheritance for the jobtitles here.
But if you are doing O/R mapping you might end up with a bit overcomplex data model behind the scenes if the mapper tries to map each sub type to a new table.
So in such cases, I often go for the enum approach if there is no real/complex behavior associated with the types.
I can live with a "if type == JobTitles.Fireman ..." if the usage is limited and it makes things easer or less complex.
e.g. the Entity Framework 4 designer for .NET can only map each sub type to a new table. and you might get an ugly model or alot of joins when you query your database w/o any real benefit.
However I do use inheritance if the type/role is static.
e.g. for Products.
you might have CD <- Product and Book <- Product.
Inheritance wins here because in this case you most likely have different state associated with the types.
CD might have a number of tracks property while a book might have number of pages property.
So in short, it depends ;-)
Also, at the end of the day you will most likely end up with a lot of switch statements either way.
Let's say you want to edit a "Product" , even if you use inheritance, you will probably have code like this:
if (product is Book)
Response.Redicted("~/EditBook.aspx?id" + product.id);
Because encoding the edit book url in the entity class would be plain ugly since it would force your business entites to know about your site structure etc.
Having an enum is like throwing a party for all those Open/Closed Principle is for suckers people.
It invites you to check if an animal is of a certain type and then apply custom logic for each type. And that can render horrible code, which makes it hard to continue building on your system.
Why?
Doing "if this type, do this, else do that" prevents good code.
Any time you introduce a new type, all those ifs get invalid if the new type is not handled. In larger systems, it's hard to find all those ifs, which will lead to bugs eventually.
A much better approach is to use small, well-defined feature interfaces (Interface segregation principle).
Then you will only have an if but no 'else' since all concretes can implement a specific feature.
Compare
if (animal is ICanFly flyer)
flyer.Sail();
to
// A bird and a fly are fundamentally different implementations
// but both can fly.
if (animal is Bird b)
b.Sail();
else if (animal is Fly f)
b.Sail();
See? the former one needs to be checked once while the latter has to be checked for every animal that can fly.
Enums are good when:
The set of values is fixed and never or very rarely changes.
You want to be able to represent a union of values (i.e. combining flags).
You don't need to attach other state to each value. (Java doesn't have this limitation.)
If you could solve your problem with a number, an enum is likely a good fit and more type safe. If you need any more flexibility than the above, then enums are likely not the right answer. Using polymorphic classes, you can:
Statically ensure that all type-specific behavior is handled. For example, if you need all animals to be able to Bark(), making Animal classes with an abstract Bark() method will let the compiler check for you that each subclass implements it. If you use an enum and a big switch, it won't ensure that you've handled every case.
You can add new cases (types of animals in your example). This can be done across source files, and even across package boundaries. With an enum, once you've declared it, it's frozen. Open-ended extension is one of the primary strengths of OOP.
It's important to note that your colleague's example is not in direct opposition to yours. If he wants an animal's type to be an exposed property (which is useful for some things), you can still do that without using an enum, using the type object pattern:
public abstract class AnimalType {
public static AnimalType Unknown { get; private set; }
public static AnimalType Cat { get; private set; }
public static AnimalType Dog { get; private set; }
static AnimalType() {
Unknown = new AnimalType("Unknown");
Cat = new AnimalType("Cat");
Dog = new AnimalType("Dog");
}
}
public class Animal {
public AnimalType Type { get; set; }
}
This gives you the convenience of an enum: you can do AnimalType.Cat and you can get the type of an animal. But it also gives you the flexibility of classes: you can add fields to AnimalType to store additional data with each type, add virtual methods, etc. More importantly, you can define new animal types by just creating new instances of AnimalType.
I'd urge you to reconsider: in an anemic domain model (per the comments above), cats don't behave differently than dogs, so there's no polymorphism. An animal's type really is just an attribute. It's hard to see what inheritance buys you there.
Most importantly OOPS means modeling reality. Inheritance gives you the opportunity to say Cat is an animal. Animal should not know if its a cat now shout it and then decide that it is suppose to Meow and not Bark, Encapsulation gets defeated there. Less code as now you do not have to do If else as you said.
Both solutions are right.
You should look which techniques applies better to you problem.
If your program uses few different objects, and doesn't add new classes, its better to stay with enumerations.
But if you program uses a lot of different objects (different classes), and may add new classes, in the future, better try the inheritance way.
When programming, we usually use some type-casting operations.
When the casting happens on the objects "on the same level", it feels ok. But when it happens on the ojects "on the different levels"(mainly between father and son), it feels weird.
Considering this:
Class Son extends Father
WhenSon s = (Son)father;, it's absolutely unreasonable. Because a "Son" is not a "Father" anymore, "Son" may grow up with some new properties "Father" doesn't have, the casting operation makes these properties unknown.
On the other hand, Father f = (Father)son seems reasonable, but according to LSP
"An instance of a derived should be able to replace any instance of its superclass"
A "Son" can do anything his "Father" can, so the casting operation seems useless.
So can i say that these casting operations are agaisnt OO design principle but necessary?
Casting objects in OOP is usually the product of either class inheritance or interface inheritance. One has to always remember that class inheritance denotes an "is a" relationship, and interface inheritance denotes a "has a" relationship.
Let's go over to your example: Class Son extends Father doesn't sound like a reasonable abstraction, since every male person is a son, but not all of them will be a father. In fact, the opposite might be more applicable: Class Father extends Son because every father is always someone's son.
On the other hand, we could just ditch this whole abstraction and say that we have, instead, a Class Person that can either be a male or a female, a mother or a father, and so on. Each person will have an attribute containing collection of Person with name Children.
In this abstraction, a Person will only be considered a Father if its gender is male and if Person.Children.Count > 0. This is much simpler than defining a Father class in and by itself.
I find questions like these always interesting and it lends itself to some kind of debate I think :)
Here are my thoughts.
In class design I would in this case create a class of Person
class Person
{
public bool IsFather { get; set; } // this variable will change depending on the count of children
public List<Person> children { get; set; }
}
When I create a derived class, the class should be a specialized person and in the case of Father and Son, I wouldn't create a Father class and a Son class.
You've got a bad example of inheritance.
A better one could be
Class Person
{
public void walk();
}
Class Man extends Person
{
public void shaveBeard();
}
Inheritance is the way to represent an is a relationship; e.g. "a Man is a Person".
Using the Objects
Either of these lines is OK.
Person p = new Person();
Person m = new Man();
It is fine to call p.walk on both. However to call the shavebeard() method, m has to be a Man object. For reasons of type safety, you have to cast it to a Man object; e.g. (Man) m.shaveBeard(); The cast here will fail, so it's best to check that the variable refers to an instance of Man:
if(m instanceof Man)
{
(Man) m.shaveBeard();
}
Neither seem reasonable to me. What seems more plausible:
Son s = (Son)person //Valid
Father f = (Father)person //Valid
Here's a hint: the Class on the left hand side is the superclass and that on the right side is the subclass so if we have this:
Object p = new Student(); //is correct
Student s = new Object() //is wrong...it's like saying that every Object is a student..
Care must be considered when doing explicit casting as it may look correct but might not work as expected. You can't instantiate your father's private property to yourself.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
Any advice on solving this problem?
Well, here's one good one that I came up with - utilizing OOP overriding, subclass and superclass:
namespace Animals{
// base class Animal
class Animal{
public void eat(Food f){
}
}
class Carnivore extends Animal{
public void eat(Meat f){
}
}
class Herbivore extends Animal{
public void eat(Plant f){
}
}
class Omnivore extends Animal{
public void eat(Food f){
}
}
}
namespace Food{
// base class Food
class Food{
}
class Meat extends Food{
}
class Plant extends Food{
}
}
I create subclasses Herbivore, Carnivore and Omnivore from the superclass Animal and override the eat method with the type of food that it can actually eat.
So:
Plant grass = new Plant();
Herbivore deer = new Herbivore();
deer.eat(grass); // ok
Plant grass2 = new Plant();
Carnivore tiger = new Carnivore();
tiger.eat(grass2); // not ok.
Meat deer2 = new Meat();
tiger.eat(deer2); // ok
Well, the final problem is that, when you specify that deer is a Herbivore, you can't make it a Meat for tiger to eat. However at the end of the day, this should be sufficient for solving the interview problem whilst not putting the interviewer to sleep.
There's a wonderful poster for the Liskov Substitution Principle that says, "If it looks like a duck, quacks like a duck, but needs batteries, you've probably got the wrong abstraction." And that's the quick answer - some of the objects can be both animals and food, so unless you're willing to go the route of multiple inheritance, then the classification schema is all wrong.
Once you've cleared that hurdle, the rest is open-ended, and you can bring in other design principles. For instance, you could add an IEdible interface that allows objects to be consumed. You might go aspect-oriented, and add decorators for carnivore and herbivore, and that would allow consumption of only the right class of objects.
The point is to be able to think on your feet, to see and explain various aspects of a problem, and to communicate well. And perhaps not to get stuck on a "one right answer" limitation.
I'd tell him to scratch that. It's a horrible abstraction. Not to mention we're not given any context. Abstractions don't come out of thin air, or out of an "idea" of what's "right". Show me what problem are you trying to solve first, so we can evaluate this abstraction.
If no context is provided, then I'll just assume/make-up my own: you want some types of objects to be able to eat other types of objects. Nothing more, nothing less.
Make an Eatable interface (or you can call it Food, if you want), and since we have no context what so ever, I'll assume it's a toy console program, that just prints:
<X> ate <Y>
so all we need for this interface is a getFoodName() method.
For error checking, you can create a bunch of isXFoodType methods, for instance, isGrassFoodType(), isMeatFoodType(), etc. The Cow's implementation of Eat(Eatable e) would check for isGrassFoodType(), and when fails, prints:
"Cow can't eat " + e.getFoodName()
Alan Kay, who coined the term "object-oriented programming", has said "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things".
Trying to fix this "problem" in the data model sounds to me like the opposite of late-binding: why do you need the compiler to enforce this? I wouldn't worry about changing the model at all. If you're passed something you can't eat, you throw an exception -- just like in real life, pretty much!
Food should be an interface, therefore Plant and Animal could be Food too.
abstract Animal class should have eat method that take Food as parameter.
subclasses of Animal: Carnivore, Herbivore and Omnivore should have their own version of eat.
For example for Carnivore:
private void eat(Food food)
{
if(food instanceof Animal)
{
happilyEat();
}
else
{
sniff&TurnAway();
}
}
The Problems solved.
But for a better design, Carnivore, Herbivore and Omnivore should be interfaces too, as they are not the proper way of tagging the animals.
This is easy with Generics in C# btw:
public class Food
{
}
public abstract class Animal<T> : Meat where T:Food
{
public abstract void Eat(T food);
}
public class Herbivore : Animal<Plant>
{
public override void Eat(Plant food)
{
Console.WriteLine("Herbivore eats plants.");
}
}
public class Omnivore : Animal<Food>
{
public override void Eat(Food food)
{
Console.WriteLine("Omnivore eats food.");
}
}
public class Carnivore : Animal<Meat>
{
public override void Eat(Meat food)
{
Console.WriteLine("Carnivore eats meat.");
}
}
public class Plant : Food
{
}
public class Meat : Food
{
}
public class Cow : Herbivore
{
}
public class Tiger : Carnivore
{
}
public class Human : Omnivore
{
}
Usage:
var human = new Human();
var tiger = new Tiger();
var cow = new Cow();
var plant = new Plant();
human.Eat(cow);
tiger.Eat(human);
cow.Eat(tiger); // this doesn't compile
tiger.Eat(plant); // neither does this
Here's some thoughts on that interview question:
I agree with Cylon Cat: This kind of abstraction doesn't work well without multiple inheritance (even if it's Java-like interfaces.)
I would create two forms of inheritance:
Animal:
Carnivore
Herbivore
Food:
Meat
Vegetable
The "eat" method of the two kinds of animals (I'm ignoring omnivores, insectivores, and many other kinds) would be specialized for the different kinds of food. If we're using a language like Java, then Food would be an interface.
Any animal is food, any vegetable is food. And in fact a tiger can be eaten by a cow. (The prion disease scrapie is spread by feeding infected sheep neural tissue to uninfected sheep.)
You could have a hierarchy of species, ala Linnaeus, both animal and vegetable. Each species is a Singleton, and it has a List<Species> that records its typical diet. Ditch the Food hierarchy entirely, it only confuses things.
And, if your only problem is recording diet for each species, then the multiple Species classes are unnecessary. Just have a single Species class with the species name as one instance variable and the List<Species> as another.
There's no one best solution. You might want to make this a community wiki, as that's generally the accepted practice for subjective questions.
I'd think about what actually would make the best parent class for the hierarchies, and try to figure out what "best" means in that context.
Everything there are Things, which could have a Name attribute. But everything there at some level is Food; if it can eat something, something can eat it. I might make Food the parent class, and give it a method that returns a boolean to check if the parameter object can eat the current object.
So,
class Food {
boolean canBeEatenBy(Food hungryObject)
String name
}
That seems the simplest class hierarchy that fits everything I might need on a first pass?
That said, the important part in most interview questions is the feel you get for the interviewee, not so much the exact answer that they give.
Double dispatch, perhaps?
If you expect the system to get very big I would suggest subclassing plant/meat and herbivore/carnivore/omnivore.
Ensure the system has a standard interface for all plant/animals called getFoodName() and getFoodType(), you could enforce this by creating a parent class for plants/animals called species.
The problem I would see with the subclassing plant/meat and carnivore/herbivore is that a meerkat is carnivore but it likely can't eat a rhino(there may be a better example), so some restrictions are needed beyond "I eat meat, you eat plants".
If it wasn't going to get incredibly big and you wanted to be neurotic about it, you could store static enums of allowable foods for each subclass of animal. So tiger could store deer, antelope etc.