views:

399

answers:

12

I am trying to wrap my head around object oriented programming.

My understanding is that we have objects so we can design our programs to mirror real-life objects.

Lets take a class hierarchy:

Fruit (base)

Apple

Fruit has a function void Eat(). Obviously you can use Fruit polymorphically if Eat() is virtual. But does this make sense? Fruit cannot eat itself!

Should a fruit object rather be passed to a human object which has a Eat() function?

I am trying to figure out the correct way to think about this. How closely, in general, should programming objects mirror real-life objects.

+4  A: 

I think you should read the SOLID principles, it's going to help you a lot. http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx

Omu
A: 

Should a fruit object rather be passed to a human object which has a Eat() function?

Yes.

But program objects are generally more abstract than this naive example. In the real world of computer programming, objects like fruit and humans would generally be represented as attributes in a database. The consumption and manipulation of such data would be done in programming objects.

Robert Harvey
A: 

You're correct that probably eat() would be a method of a human or mammal or fruitFly. A fruit itself probably has no behaviour, and hence no methods.

I don't often consider the benefits of OO in realtion to the mapping from real things to Object. That's probably because we deal with less tangible concepts such as Invoices and Orders.

I see the primary win from OO to be in the structure it brings to my code. In the real world Invoices and Orders don't actually do anything, but in my code they do. So the programmtic Order may well be much closer to the combination of the data representing an order, and some human business process relevent to the order.

djna
+8  A: 

You've got a design problem -- as you correctly point out, Eat() doesn't make obvious sense as a member of Fruit. On the other hand, an "edible" attribute would make more sense. As would an "onEaten" event, etc. What your fruit/apple classes expose (and what other objects make sense in your model) depends on a lot of other factors, including what you're trying to accomplish with those constructs in your application.

In general you want your classes to represent logical domain level entities. Sometimes those correspond to a physical entity in the real world, but in many cases they don't.

In my opinion OO problem decomposition is something programmers are generally pretty bad at. I don't know how many times I've seen the equivalent of a car derived from a steering wheel and shaken my head while the original developer couldn't wrap their head around why their design didn't make a lot of sense.

Rob Pelletier
+1 Good examples. Another example would be that Fruit is of Edible interface/class and a consumer object, such as an object of Person class, has the Eat(Edible e) method. Which would lead to myPerson.Eat(myEdible) much like the OP was speculating.
Spoike
protected virtual void OnEaten() { Barf(); }
Andrei Rinea
A: 

Most human languages follow a sentence structure (For simple statements) of Subject Verb Object

In OOP languages like c++, not entirely ironically, the object is the object, and it comes first, so the normal order is reversed:

So this is the 'basic' pattern in c++ :-

object.verb( subject );

This is actually rubbish. Most classes are designed with a subject.verb(object) interface. Knowledge of SVO however does allow one to test whether or not a class interface has been designed to be the correct way around (in this case, it has not).

That said, there are a large number of human languages that are naturally OVS, or some other variant where the typical english order is reversed. When dealing with internationally developed software other developers might have a different idea as to the correct and normal order of subjects, and objects in a simple statement.

Chris Becke
Seems like you're backwards in this case, and in general. human.eat(apple) is what we'd be looking at here. Subject.verb(object).
Grumdrig
Actualy OSV and OVS languages do seem bloody rare. Even more support for formalizing SVO as the proper order in OOP design.
Chris Becke
+1  A: 

Assuming you were, say, writing a hungry people simulator, then I think it would make much more sense to, as you say, have a Human::Eat(Fruit f) function. Your Fruit might not have methods since Fruit doesn't do much on it's own, but it might have a calories property, and so on.

Grumdrig
A: 

I tend to think about:

Is a

Has a

So, an apple is a fruit, so inheritance makes sense.

But, fruit has (is) eatable may make sense, but that shows it is a property of fruit, not an action (method).

For example, you may have unripened apple, that would not be edible (eatable) so you could then set this property.

Now, whatever is going to eat this you could set whether an apple is part of it's diet.

Now Has a would be for composition. So, an apple has a seed would mean that seed doesn't extend apple, but an apple would have a collection of seeds.

So, you do have a design problem, and I hope that these two concepts may help to clarify.

James Black
"Is-a" (inheritance) and "has-a" (composition) analogy is a good start to think about things, but it is worth noting that this has some limitations. It's better to go for the "prefer composition over inheritance" than think everything about "is-a and has-a" since the latter leads to preference for inheritance. It is difficult to maintain large inheritance structures.
Spoike
@Spoike - when a person is new to OOP then keeping it simple is the best bet, and these two concepts I think help to keep it simpler. Once these are better understood then you can look at other approaches to understand, IMO.
James Black
+3  A: 

Simply mirroring real-world objects is rarely a good idea. To borrow from a classic example - software that controls a coffeemaker is not about coffee beans and hot water - it's about making coffee.

You need to find the underlying abstraction to your real-world problem, not just copy nouns into object hierarchies.

If your apple derives from fruit, does it add any interesting behavior? Is the hierarchy really needed? Inheritance adds a level of complexity to your software and anything increasing complexity is bad. Your software is just a bit harder to follow and understand, there's just a bit more to cover in your test and the likelihood of a bug is just a tiny bit larger.

I find OOP is more about the whitespace - what you are leaving out is more important.

Goran
A: 

How closely, in general, should programming objects mirror real-life objects.

Not much, only enough.

One of the main characteristics of OOP, is abstraction. You don't need to have all the attributes/methods of an object to be able to use it.

You just need to the basic to use it.

The whole thing about objects, is to have the data and the functions that perform something about that data in the same place.

So in your fruit class I would better have something as Color or an indication if it would be eaten. For instance:

 Fruit
     + color : Color
     - isMature : Boolean

     + canBeEaten() : Boolean
          return isMature

That way you may create different fruits

 apple = Fruit()
 appe.color = Color.red
 out.print( "Can this fruit be eaten? %s ", apple.canBeEaten() )

 orange = Fruit()
 orage.color = Color.orange
 out.print( "Can this fruit be eaten? %s ", orange.canBeEaten() )

Etc.

If you see the attributes ( color and isMature ) are stored inside the object. That way you don't have to keep track of their state from outside.

As for inheritance, it only makes sense when you need to add a new behaviour to some method, and yes, the methods are relative to the attributes or characteristics of the object. As you point out fruit.eat() doesn't make much sense.

But consider a method to get the Juice from the fruit.

Fruit
    + getJuice(): Juice

Apple -> Fruit
     + getJuice(): Juice
         // do what ever is needed internally to create the juice

Orange -> Fruit
    + getJuice(): Juice
        // here, certainly the way to get the juice will be different
OscarRyz
A: 

I always find examples using 'animals' or 'fruit' counter-intuitive. People are difficult objects to model, and it's unlikey that you'll encounter applications with the same requirements.

Using the concept of anthropomorphisation can really help assign reponsibilities. Basically, imagine that your object is a person (I typically sketch them with a face and limbs during design sessions).

Now you can ask these questions about your object:

  • "What responsibilites does my object have within the system?"
  • "Is this object responsible for doing xxx, or should xxx be the responsibility of another object?"
  • "Is this object doing more than it should be?" (e.g. if it's design to calculate something, it shouldn't be responsible for loading values)

"Object Thinking" by David West is a good book to read on the topic.

Vijay Patel
+1  A: 

Something of class Herbivore would have an Eat function, as would something of class Carnivore, but each one's Eat would have some differing restrictions on what argument could be passed to the Eat function. The Fruit is what is eaten, so it would be passed as the argument to Herbivore.Eat(), whereas you would want to pass an object of type Hamburger to Carnivore.Eat(), and raise an exception if Hamburger were passed to Herbivore.Eat().

But really, I don't think OOP is so we can model software objects to be just like real-life objects. I've found that most of my OOP design works with pretty abstract objects, and only with respect to the system they are part of. If I wrote a library checkin/checkout system, I would model a Book in terms of its administrative properties and functions - I would not model it as a collection of Page objects, and I doubt I would even define anything like a Read() method, although such is the main purpose of having a book in the first place. The Book object's role in the system dictates my design much more than what one does with books in the real world.

Paul McGuire
A: 

My understanding is that we have objects so we can design our programs to mirror real-life objects.

Maybe more like 'relate' them to real life objects, where applicable.

Should a fruit object rather be passed to a human object which has a Eat() function?

Yes, or something more general than a human.

I am trying to figure out the correct way to think about this. How closely, in general, should programming objects mirror real-life objects.

Only define what you need to define. Then the implementation (typically) becomes very obvious. In other words, you're probably thinking too hard.

Justin