Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Suppose you have the following:

     +--------+     +------+
     | Animal |     | Food |
     +-+------+     +----+-+
       ^                 ^
       |                 |
       |                 |
  +------+              +-------+
  | Deer |              | Grass |
  +------+              +-------+

Deer inherits from Animal, and Grass inherits from Food.

So far so good. Animal objects can eat Food objects.

Now lets mix it up a bit. Lets add a Lion which inherits from Animal.

     +--------+     +------+
     | Animal |     | Food |
     +-+-----++     +----+-+
       ^     ^           ^
       |     |           |
       |     |           |
  +------+ +------+     +-------+
  | Deer | | Lion |     | Grass |
  +------+ +------+     +-------+

Now we have have a problem because Lion can eat both Deer and Grass, but Deer is not Food it is Animal.

With out using multiple inheritance, and using object oriented design, how do you solve this problem?

FYI: I used http://www.asciiflow.com to create the ASCII diagrams.

share|improve this question
11  
Modelling the real world is usually a problem sooner or later, because there is always something strange going on (like flying fish, a fish or a bird? but a penguin is a bird, cannot fly and eats fish). What @Ampt says sounds plausible, an Animal should have a collection of stuff it eats. –  Rob van der Veer Jul 19 '13 at 20:09
2  
I think Animal should inherit from Food. If something tries to eat a Lion, just throw an InvalidOperationException. –  RalphChapin Jul 19 '13 at 20:35
4  
@RalphChapin: All kinds of things eat lion (vultures, bugs, etc). I think animal and food are artificial distinctions that will break down because they're not broad enough (all animals are some other animals food, eventually). If you classed on "LivingThing" you'd only have to deal with the edge cases with plants that eat non-living things (minerals,etc), and it wouldn't break anything to have LivingThing.Eat(LivingThing). –  Satanicpuppy Jul 19 '13 at 20:40
2  
Sharing your research helps everyone. Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. Also see How to Ask –  gnat Jul 19 '13 at 20:59
8  
This question has been answered by the game Age of Empire III. ageofempires.wikia.com/wiki/List_of_Animals Deer and Gazelle implement IHuntable, Sheep and Cow are IHerdable (controllable by human), and Lion only implements IAnimal, which does not imply any of those interfaces. AOE3 supports querying the set of interfaces supported by a particular object (similar to instanceof) which allows a program to query its capabilities. –  rwong Jul 20 '13 at 2:25

10 Answers 10

up vote 31 down vote accepted

IS A relationships = Inheritance

Lion is an animal

HAS A relationships = Composition

Car has a wheel

CAN DO relationships = Interfaces

ICanEat

share|improve this answer
4  
+1 That is so simple and yet such a good summary of the 3 different relationship types –  dreza Jul 20 '13 at 11:25
4  
Alternative: ICanBeEaten or IEdible –  Mike Weller Jul 31 '13 at 7:36
    
CAN HAZ relationships = lolcats –  Steven A. Lowe Jan 31 at 17:48

OO is just a metaphor that patterns itself after the real world. But metaphors only go so far.

Normally there is no right way to model something in OO. There is a right way to do it for a particular problem in a particular domain and you shouldn't expect it to work well if you change your problem, even if the domain objects are the same.

I think this is a common misconceptions most Comp. Eng. students have in their first years. OO is not a universal solution, just a decent tool for some kind of problems that can model your domain reasonably well.

I didn't answer the question, precisely because we lack domain info. But with the above in mind you might be able to design something that suits your needs.

share|improve this answer
1  
+1 OO is a tool, not a religion. –  mouviciel Jul 19 '13 at 21:00
    
I agree, there may not perfect solution if this problem continues to change and evolve. In it's current state does this problem lack domain info to come up with a solution? –  Michael Irey Jul 19 '13 at 21:19

My design would be like this:

  1. Foods are declared as interfaces; there is an IFood interface and two derivated interfaces from it: IMeat and IVegetable
  2. Animals implement IMeat and Vegetables implement IVegetable
  3. Animals have two descendants, Carnivores and Hebivores
  4. Carnivores have the Eat method that receives an instance of IMeat
  5. Herbivores have the Eat method that receives an instance of IVegetable
  6. Lion descends from Carnivore
  7. Deer descends from Herbivore
  8. Grass descends from Vegetable

Because Animals implement IMeat and Deer is an (Herbivore) Animal, Lion, which is a (Carnivore) Animal that can eat IMeat can also eat Deer.

Deer is a Herbivore, so it can eat Grass because it implements IVegetable.

Carnivores can´t eat IVegeable and Herbivores can´t eat IMeat.

share|improve this answer
1  
I'm seeing a lot of enumeration types here using inheritance just to constrain when the types being inherited implement nothing... Whenever you find yourself making types that don't implement any functionality at all, it's a giveaway something's a foot; you've expanded a model in the type system that yields no value to the usability in code –  Jimmy Hoffa Jul 19 '13 at 21:34
    
Remember than omnivores exist, like humans, apes and bears. –  user61852 Jul 19 '13 at 23:46
    
So how do you add that both, lions and deer, are mammals? :-) –  johannes Jul 20 '13 at 2:08
2  
@JimmyHoffa Those are called "marker interfaces" and are a totally valid use of interface. It needs to be code-reviewed to decide whether the use is justified, but there are many use cases (such as this one, where a Lion trying to eat Grass would throw a NoInterface exception). The marker interface (or the lack of) serves to foretell an exception that will be thrown if a method is called with unsupported arguments. –  rwong Jul 20 '13 at 2:30
1  
@rwong I understand the concept, never heard it formalized before; just my experience has been everytime a code base I've been working in has them it makes things more complex and harder to maintain. Perhaps my experience is however just been where people used them wrong. –  Jimmy Hoffa Jul 20 '13 at 16:11

You want to further break down animals into their sub classes (or at least as far as makes sense for what you're doing). Given that you are working with what look like basic animals and two types of food (plants and meat), it makes sense to use carnivores and herbivores to further define an animal and keep them seperate. Here's what I drew up for you.

             +----------------+                   +--------------------+
             |    Animal      |                   |      Food          |
             |----------------|<--+Interfaces+--->|--------------------|
             |                |                   |                    |
             +----------------+                   +--------------------+
                +           +                       +                 +
                |           |    Abstract Classes   |                 |
                |           |        |          |   |                 |
                v           v        v          v   v                 v
   +-----------------+  +----------------+     +------------+      +------------+
   |   Herbivore     |  |  Carnivore     |     |   Plant    |      |   Meat     |
   |-----------------|  |----------------|     |------------|      |------------|
   |Eat(Plant p)     |  |Eat(Meat m)     |     |            |      |            |
   |                 |  |                |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
            +                    +                    +                   +
            |                    |                    |                   |
            v                    v                    v                   v
   +-----------------+  +----------------+     +------------+      +------------+
   |  Deer           |  |   Lion         |     |  Grass     |      |  DeerMeat  |
   |-----------------|  |----------------|     |------------|      |------------|
   |DeerMeat Die()      |void Kill(Deer) |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
                                 ^                    ^
                                 |                    |
                                 |                    |
                              Concrete Classes -------+

As you can see, they both expose an eat method, but what they eat changes. The Lion can now kill a deer, the deer can die and return DeerMeat, and OPs original question of how to allow a lion to eat a deer but not grass is answered without engineering an entire ecosystem.

Of course, this gets interesting very quickly because a Deer could be considered a type of meat as well, but to keep things simple, I would create a method called kill() under deer, which returns a deer meat, and put that as a concrete class extending meat.

share|improve this answer
    
Would Deer then expose an IMeat interface? –  Dan Pichelman Jul 19 '13 at 20:21
    
Meat isn't an interface, it's an abstract class. I added how I would implement that for you –  Ampt Jul 19 '13 at 20:24
    
Eat(Plant p) and Eat(Meat m) both violate LSP. –  user61852 Jul 19 '13 at 20:29
    
How so @user61852 ? I purposely didn't expose Eat in the animal interface so that each type of animal could have it's own eat method. –  Ampt Jul 19 '13 at 20:36
1  
TCWL (Too complex, will leak). The problem is distributed and emergent and your solution is static, centralized and pre-defined. TCWL. –  user61852 Jul 31 '13 at 13:40

What foods an animal can eat don't actually form a hierarchy, in this case nature failed inexcusably to conform to simple object oriented modelling (note that even if it did, animal would have to inherit from food, since it is food).

Knowledge of what foods an animal can eat can't live entirely with either of the classes, so simply having a reference to some member of the food hierarchy can't be enough to tell you what things you can eat.

It's a many to many relationship. This means every time you add an animal, you need to figure out what it can eat, and every time you add a food, you need to figure out what can eat it. Whether there is further structure to exploit depends on what animals and foods you are modeling.

Multiple inheritance doesn't really solve this very well either. You need some kind of collection of things an animal can eat, or of animals that can eat a food.

share|improve this answer
    
Like they say about regex "I had a problem so I used regex, now I have two problems", MI is along the lines of "I had a problem so I used MI, now I have 99 problems" If I were you I'd follow that vain you were poking at here though of the food knowing what can eat it, this actually simplifies the model a ton. Dependency inversion FTW. –  Jimmy Hoffa Jul 19 '13 at 21:41

I will approach the problem from different side : OOP is about behavior. In your case, does Grass have some behavior to be child of Food? So in your case, there won't be Grass class, or at least, it won't be inherited from Food. Also, if you need to enforce who can eat what at compile time, it is questionable if you need Animal abstraction. Also, it is not rare to see carnivores eating grass, albeit not for sustenance.

So I would design this as (not going to bother with ASCI art):

IEdible with property Type, which is enum of meat, plant, carcass, etc.. (this won't change often and doesn't have any specific behavior, therefore there is no need to model this as class hiearchy).

Animal with methods CanEat(IEdible food) and Eat(IEdible food), which are logical. Then, specific animals can check whenever then can eat given food in given circumstances and then eat that food to gain sustenance/do something else. Also, I would model classes Carnivore, Herbivore, Omnivore as Strategy pattern, than as part of animal hierarchy.

share|improve this answer

TL;DR: Design or model with a context.

I think your question is difficult because it lacks context of the actual problem you are trying to solve. You have some models and some relationships, but lack the framework in which it needs to work. Without context, modeling and metaphors don't work well leave the door open to multiple interpretations.

I think it more productive to focus on how the data will be consumed. Once you have the pattern of data usage it's easier to work backwards to what the models and relationships should be.

For example more detailed requirements will necessitate different object relationships:

  • support Animals eating non-Food like Gastroliths
  • support Chocolate as Poison for Dogs, but not for Humans

If we start in on the exercise of how to model the simple relationship presented the Food Interface may be best; and if that is the sum total how the relationships in the system then your fine. However, just a few additional requirements or relationships can vastly affect the models and relationships that worked in the simpler case.

share|improve this answer
    
I agree but its just an small example an were not trying to model the world. For example you can have a Shark that eats Tires and License Plates. You can just make a parent abstract class with a method that eats any kind of object and Food could extend this abstact class. –  hagensoft Jul 30 '13 at 22:49
    
@hagensoft: Agreed. I do get carried away sometimes because I constantly see developers modeling based on a metaphor they immediately seized upon, rather than looking at the how the data needs to be consumed and used. They get married to an OO design based on an initial idea and then try and force the problem to fit their solution instead of of making their solution fit the problem. –  dietbuddha Jul 31 '13 at 0:07

With out using multiple inheritance, and using object oriented design, how do you solve this problem?

Like most things, it depends.

It depends on what you see 'this problem' to be.

  • Is it a general implementation problem, e.g. how to 'get around' the absence of multiple inheritance in your chosen platform?
  • Is it a design problem just for this specific case, e.g. how to model the fact that animals are also food?
  • Is it a philosophical problem with the domain model, e.g. are 'food' and 'animal' valid, necessary, and sufficient classifications for the envisioned practical application?

If you're asking about the general implementation problem, the answer will depend on the capabilities of your environment. IFood and IAnimal interfaces could work, with an EdibleAnimal subclass implementing both interfaces. If your environment doesn't support interfaces, just make Animal inherit from Food.

If you're asking about this specific design problem, just make Animal inherit from Food. It's the simplest thing that could possibly work.

If you're asking about these design concepts, the answer strongly depends upon what you intend to do with the model. If it's for a dog-eat-dog video game or even an application to track feeding schedules at a zoo, it might be enough to work. If it's for a conceptual model for animal behavioral patterns, it's probably a tad shallow.

share|improve this answer

Inheritance should be used for something that is always something else, and cannot change. Grass is not always food. For example, I do not eat grass.

Grass plays the role of a foodstuff for certain animals.

share|improve this answer
    
Its just an abstraction. If that's a requirement then you could create more divisions that extend the Plant abstract class and make humans eat an abstract class like 'HumanEatablePlants' which would group the plants humans do eat into concrete classes. –  hagensoft Jul 30 '13 at 22:58

You have just come across the basic limitation of OO.

OO works well with hierarchical structures. But once you get away from strict hierarchies the abstraction does not work so well.

I know all about metamorphosis compositions etc. that are used to get around these limitations but they are clumsy, and, more importantly lead to obscure and hard to follow code.

Relational data bases were invented primarily to get away from the limitations of strict hierarchical structures.

To take your example grass could also be a building material, a raw material for paper, a clothing material, a weed or a crop.

A deer could be a pet, livestock, a zoo animal or a protected species.

A lion could also be a zoo animal or a protected species.

Life is not simple.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.