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.
Let's take a class hierarchy:
class Fruit {
void Eat() {
}
}
class Apple extends Fruit {
}
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?
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:That way you may create different fruits
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.
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.
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.
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.
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.
Maybe more like 'relate' them to real life objects, where applicable.
Yes, or something more general than a human.
Only define what you need to define. Then the implementation (typically) becomes very obvious. In other words, you're probably thinking too hard.