views:

274

answers:

11

This is more of a subjective question, so I'm going to preemptively mark it as community wiki.

Basically, I've found that in most of my code, there are many classes, many of which use each other, but few of which are directly related to each other. I look back at my college days, and think of the traditional class Cat : Animal type examples, where you have huge inheritance trees, but I see none of this in my code. My class diagrams look like giant spiderwebs, not like nice pretty trees.

I feel I've done a good job of separating information logically, and recently I've done a good job of isolating dependencies between classes via DI/IoC techniques, but I'm worried I might be missing something. I do tend to clump behavior in interfaces, but I simply don't subclass.

I can easily understand subclassing in terms of the traditional examples such as class Dog : Animal or class Employee : Person, but I simply don't have anything that obvious I'm dealing with. And things are rarely as clear-cut as class Label : Control. But when it comes to actually modeling real entities in my code as a hierarchy, I have no clue where to begin.

So, I guess my questions boil down to this:

  1. Is it ok to simply not subclass or inherit? Should I be concerned at all?
  2. What are some strategies you have to determine objects that could benefit from inheritance?
  3. Is it acceptable to always inherit based on behavior (interfaces) rather than the actual type?
+9  A: 

Inheritance should always represent an "is-a" relationship. You should be able to say "A is a B" if A derives from B. If not, prefer composition. It's perfectly fine to not subclass when it is not necessary.

For example, saying that FileOpenDialog "is-a" Window makes sense, but saying that an Engine "is-a" Car is nonsense. In that case, an instance of Engine inside a Car instance is more appropriate (It can be said that Car "is-implemented-in-terms-of" Engine).

For a good discussion of inheritance, see Part 1 and Part 2 of "Uses and Abuses of Inheritance" on gotw.ca.

In silico
+3  A: 
  1. As long as you do not miss the clear cut 'is a' relationships, it's ok and in fact, it's best not to inherit, but to use composition.

  2. is-a is the litmus test. if (Is X a Y?) then class X : Y { } else class X { Y myY; } or class Y { X myX; }

  3. Using interfaces, that is, inheriting behavior, is a very neat way to structure the code via adding only the needed behavior and no other. The tricky part is defining those interfaces well.

Vinko Vrsalovic
Is-a is not quite clear-cut enough. Is a ReadingWritingDatabase a ReadingDatabase? Is a CRUDDatabase a ReadingWritingDatabase? They are, but that does not mean you should have such an inheritance tree.
Sjoerd
Your example does not make much sense to me. I think that if you have such a set of classes where is-a doesn't lend itself to inheritance, you should rethink your classes. Of course there are exceptions, but is-a is a very strong indicator, you should be very aware why aren't you using it.
Vinko Vrsalovic
@Sjoerd: couldn't a ReadingWritingDatabase be subclassed from a ReadingDatabase and extend the functionality to writing by implementing the necessary methods?
JAB
+2  A: 

I also hate the Dog -> Mammal -> Animal examples, precisely because they do not occur in real life.

I use very little subclassing, because it tightly couples the subclass to the superclass and makes your code really hard to read. Sometimes implementation inheritance is useful (e.g. PostgreSQLDatabaseImpl and MySQLDatabaseImpl extend AbstractSQLDatabase), but most of the time it just makes a mess of things. Most of the time I see subclasses the concept has been misused and either interfaces or a property should be used.

Interfaces, however, are great and you should use those.

Sjoerd
Dog -> Mammal -> Animal might appear if you were building an app for a pet shop or a zoo. ;-)
Paul Sasik
Even if building a zoo app you shouldn't have Dog -> Mammal -> Animal!
DanDan
http://en.wikipedia.org/wiki/African_Wild_Dog <= dog that could be at a zoo
Matthew Whited
Even if you are building a system which is about physical animals, it may still be easier to use an Animal class which has TakeForWalk in its actions array and MeatPieces as the food property. That a dog is a mammal is a animal does not mean your class structure should be like that.
Sjoerd
+1  A: 

IMHO, you should never do #3, unless you're building an abstract base class specifically for that purpose, and its name makes it clear what its purpose is:

class DataProviderBase {...}
class SqlDataProvider : DataProviderBase {...}
class DB2DataProvider : DataProviderBase {...}
class AccountDataProvider : SqlDataProvider {...}
class OrderDataProvider : SqlDataProvider {...}
class ShippingDataProvider : DB2DataProvider {...}

etc.

Also following this type of model, sometimes if you provide an interface (IDataProvider) it's good to also provide a base class (DataProviderBase) that future consumers can use to conveniently access logic that's common to all/most DataProviders in your application model.

As a general rule, though, I only use inheritance if I have a true "is-a" relationship, or if it will improve the overall design for me to create an "is-a" relationship (provider model, for instance.)

Toby
+3  A: 

No technology or pattern should be used for its own sake. You obviously work in a domain where classes tend to not benefit from inheritance, so you shouldn't use inheritance.

You've used DI to keep things neat and clean. You separated the concerns of your classes. Those are all good things. Don't try and force inheritance if you don't really need it.

An interesting follow-up to this question would be: Which programming domains do tend to make good use of inheritance? (UI and db frameworks have already been mentioned and are great examples. Any others?)

Paul Sasik
+1 for the followup question. One other main area I've seen (and used) it is in the Decorator pattern.
drharris
A: 

The answer to each of your 3 questions is "it depends". Ultimately it will all depend on your domain and what your program does with it. A lot of times, I find the design patterns I choose to use actually help with finding points where inheritance works well.

For example, consider a 'transformer' used to massage data into a desired form. If you get 3 data sources as CSV files, and want to put them into three different object models (and maybe persist them into a database), you could create a 'csv transformer' base and then override some methods when you inherit from it in order to handle the different specific objects.

'Casting' the development process into the pattern language will help you find objects/methods that behave similarly and help in reducing redundant code (maybe through inheritance, maybe through the use of shared libraries - whichever suits the situation best).

Also, if you keep your layers separate (business, data, presentation, etc.), your class diagram will be simpler, and you could then 'visualize' those objects that aught to be inherited.

Assaf
-1 for "it depends". That is the answer to every question.
Sjoerd
@Sjoerd, doesn't stop it from being the correct answer
Matthew Whited
+1  A: 

Where you have shared functionality, programming to the interface is more important than inheritance.

Essentially, inheritance is more about relating objects together.

Most of the time we are concerned with what an object can DO, as opposed to what it is.

class Product
class Article
class NewsItem

Are the NewsItem and Article both Content items? Perhaps, and you may find it useful to be able to have a list of content which contains both Article items and NewsItem items.

However, it's probably more likely you'll have them implement similar interfaces. For example, IRssFeedable could be an interface that they both implement. In fact, Product could also implement this interface.

Then they can all be thrown to an RSS Feed easily to provide lists of things on your web page. This is a great example when the interface is important whereas the inheritance model is perhaps less useful.

Inheritance is all about identifying the nature of Objects Interfaces are all about identifying what Objects can DO.

Atømix
+2  A: 

Generally, favour composition over inheritance. Inheritance tends to break encapsulation. e.g. If a class depends on a method of a super class and the super class changes the implementation of that method in some release, the subclass may break.

At times when you are designing a framework, you will have to design classes to be inherited. If you want to use inheritance, you will have to document and design for it carefully. e.g. Not calling any instance methods (that could be overridden by your subclasses) in the constructor. Also if its a genuine 'is-a' relationship, inheritance is useful but is more robust if used within a package.

See Effective Java (Item 14, and 15). It gives a great argument for why you should favour composition over inheritance. It talks about inheritance and encapsulation in general (with java examples). So its a good resource even if you are not using java.

So to answer your 3 questions:

Is it ok to simply not subclass or inherit? Should I be concerned at all? Ans: Ask yourself the question is it a truly "is-a" relationship? Is decoration possible? Go for decoration

// A collection decorator that is-a collection with 
public class MyCustomCollection implements java.util.Collection {
    private Collection delegate;
    // decorate methods with custom code
}

What are some strategies you have to determine objects that could benefit from inheritance? Ans: Usually when you are writing a framework, you may want to provide certain interfaces and "base" classes specifically designed for inheritance.

Is it acceptable to always inherit based on behavior (interfaces) rather than the actual type? Ans: Mostly yes, but you'd be better off if the super class is designed for inheritance and/or under your control. Or else go for composition.

naikus
A: 

My class hierarchies tend to be fairly flat as well, with interfaces and composition providing the necessary coupling. Inheritance seems to pop up mostly when I'm storing collections of things, where the different kinds of things will have data/properties in common. Inheritance often feels more natural to me when there is common data, whereas interfaces are a very natural way to express common behavior.

Dan Bryant
A: 

I wouldn't get too worried about how your class diagram looks, things are rarely like the classroom...

Rather ask yourself two questions:

  1. Does your code work?

  2. Is it extremely time consuming to maintain? Does a change sometimes require changing the 'same' code in many places?

If the answer to (2) is yes, you might want to look at how you have structured your code to see if there is a more sensible fashion, but always bearing in mind that at the end of the day, you need to be able to answer yes to question (1)... Pretty code that doesn't work is of no use to anybody, and hard to explain to the management.

Paddy
A: 

IMHO, the primary reason to use inheritance is to allow code which was written to operate upon a base-class object to operate upon a derived-class object instead.

supercat