wood burning stoves 2.0*
The moose likes OO, Patterns, UML and Refactoring and the fly likes Effective OOD Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of OCA/OCP Java SE 7 Programmer I & II Study Guide this week in the OCPJP forum!
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "Effective OOD" Watch "Effective OOD" New topic
Author

Effective OOD

Andrew Laughlin
Greenhorn

Joined: Jan 02, 2007
Posts: 19
I don't understand how to effectively use inheritance and polymorphism in OO designs. More specifically I don't see the benefit of using inheritance when derived classes will vary widely. Hopefully the code below will illustrate my confusion. This code may look a little like C#, pay it no mind.



We want to take advantage of polymophism and use Animal types freely throughout the app. This is what I don't understand. Bears don't usually have tails (at least in this example) and bulls (usually) do. How then does the client of Animal do this:

Animal animal = new Animal();
animal.Tail.Wag();


Should Animal contain a Tail and implement default behaviour for it? If so, should every derived classes' unique members get put into the base class? If that's the case it seems we force all derived objects who don't implement everything in the base class, to use a null pattern of some type to ensure the Liskov Substitution Principle is adhered to. This forces a type of transitive dependency that can't be good.

How can an Animal object represent all animals when clearly, in reality, types of animals vary widely. In general should inheritace hierarchies be very small and specific so derived classes don't deviate much, if at all? The example above is one of, well billions. I'm hoping for a general answer that is broadly applicable.

Any help is appreciated.

Best Regards,
Andrew
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
Hierarchies in the real world often make bad OO inheritance models. It always gets tough when we use examples like that. You could start with an essentially empty Animal class, say with only boolean isAlive(), and duplicate the kingdom, phylum, genus, whatever tree that scientists use, adding appropriate features as you go. But deep hierarchies like that usually don't make very satisfying software.

We often recommend a shallow hierarchy, maybe an abstract base class and some concrete classes that extend the base. The base class is a nice place to put common behavior for all concrete classes - something that you found doesn't work well for Animal and doesn't always work in software. It's also a nice place to orchestrate a sequence of steps through calls to abstract methods that concrete classes are required to override and fill in the blanks.

We had another long thread recently about putting methods into the base that are only needed by some concrete classes. It can be icky but the workarounds can be icky, too. That kind of problem always makes us want to back up a step and see if the abstract class represents the right thing.

Sorry that wasn't a realy helpful answer - no solid recommendations for what to do next. But it was a really nifty observation and question. When it comes up again in a real software design, come back and see what kind of alternatives we come up with.
[ January 12, 2007: Message edited by: Stan James ]

A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Alik Elzin
Greenhorn

Joined: Sep 19, 2002
Posts: 15
Hey Andrew.

You are right about everything you wrote.

Designing is difficult and fun

What I can say is that first you are very correct when you write interfaces and not classes (implementations).
It is always good to think about objects as interfaces for a whole lot of reasons such as encapsulation, modulation,...

Regarding the specific Animal example:
I think that one should not put a method into an interfaces if the method does not belong to it.
It is logical and will help you not breaking LSP later.
If an Animal interface would have a tail and not all animals that extends it would have a tail, such as humans and bears, how could humans and bears wag the tail ?

If you would like all animals that have a tail (not including humans, bears, ...) to be able to wag their tail, than:
Create an interface that identifies all the animals that have a tail and give some methods to use the tail.
Example:

Interface Tailed extends Animal {
void wagTail();
void raiseTail();
void lowerTail();
}

interface Dog extends Tailed {
// Dog's specific methods
}

class DogImpl implements Dog {
public void wagTail() {
//implementation...
}
//Other methods implementations...
}

Now consider the case of having a collection of animals and you the ones with a tail to wag it.
One solution would be to traverse the animals and for each animal ask if it is Tailed (animal instance of Tailed).
If so, you can cast the animal to Tailed and invoke tailed.wagTail().
This solution is ok but have a big disadvantage of using instance of.
It is a good practice to avoid instance of whenever possible.

Another solution can be to use a visitor.
With a visitor you need to write a bit more code and it needs to know all of the types in compilation type but i think is more strong and more modular.

Usually, one would have at least one method in a base interface, unless it is a markup interface.

Hope this was helpful.
Regards,
Alik.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Andrew,

I think your question actually cannot be answered, because it didn't contain a problem statement. That is, before we actually know what problem the code should solve, we can't decide what an appropriate design would look like.

For example, part of our problem could call for the animal to show enjoyment. I a procedural design, we might ask the animal whether it has a tail, and if so, have it wag.

To make use of polymorphism with an OO design, it would probably be more appropriate to have a showEnjoyment() method on Animal, and have the animals implement it differently. That a dog shows enjoyment by wagging then would be a hidden implementation detail, encapsulated in the Dog class.

Does that help?


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Jeevan Philip
Ranch Hand

Joined: Nov 17, 2006
Posts: 41
Ilja,

I second you completely. It is the "business behaviour" that need to be considered during design. How it is done (wagging tail, smiling, pantin g..etc) is an implementation detail and should be left for individual animals to specify, for ex, cats wag tail when they are unhappy.

In this case we should identify the high level behaviours of animals like "expressing emotions", producing sound, movement, feeding etc. and define such common methods in the Animal class. Behaviors specific to certain animals (thinking, dancing etc) can be made as separate Interfaces and those animals can implement them.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Effective OOD