开发者

Object oriented programming - class design confusion

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 Fr开发者_JAVA百科uit {

}

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?


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.


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.


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


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.


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.


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.


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.


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.


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


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.


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.


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.


I don't think we should try to "mirror real-life objects". I think it's more of finding real life objects that closely resemble the behavior being modeled within the context of the system (domain). A Fruit class in a game where you slice fruit for points might have drastically different behaviors and attributes than the Fruit class in a game where a character runs around collecting fruits for points; or a simulation of people eating fruits. Assigning behaviors to classes named after real life objects makes it easier to assume the behavior of code modules and speculate on their interactions.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜