• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Effective OOD

 
Greenhorn
Posts: 19
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
(instanceof Sidekick)
Posts: 8791
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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 ]
 
Greenhorn
Posts: 15
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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?
 
Ranch Hand
Posts: 41
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
Don't get me started about those stupid light bulbs.
reply
    Bookmark Topic Watch Topic
  • New Topic