Programming Tips #4 “Aggregation over Inheritance”

feature photo

If you're new here, you may want to subscribe to my RSS feed. Thanks for visiting!

managermaths.jpg

Next up on my programming tips series we have another guest post. This one is written by James Gordan.

My favourite rule is to favour aggregation over inheritance.

I can’t count how many times I’ve seen inheritance over-used, even by experienced programmers, the net result being a bunch of subclasses that become very brittle over time, by which I mean they become hard to test, hard to maintain and hard to extend.

For things like the Java API, especially the IO library, I think the class hierarchy (generally speaking) works nicely, with the intermediate and concrete classes extending the super classes quite neatly.

However, I don’t think there are too many business/application-layer concepts that model so neatly into such a deep inheritance hierarchy with such a convenient layering of behaviours as you move down the hierarchy. Many of the examples in tutorials about inheritance are contrived and/or unrealistic and whilst they may teach the programming aspect of inheritance quite effectively, I worry that many programmers take away the incorrect notion that deep hierarchies are a Good Thing.

But you know the saying… give a man a hammer…

-

If you have any programming tips that you would like to share with my readers please send them in via my contact form. I will be picking a ‘best tip’ which will be rewarded with a license for Zend Studio Professional.

There Are 21 Responses So Far. »

  1. While inheritance can easily be abused so can aggregation, it’s worth to keep in mind that in php aggregation is at run-time while inheritance is at compile-time. I’d give a link to where I read an in depth article on it but I can’t for the life of me remember where it was.

  2. I don’t mind deep trees as much as I mind tight coupling — which is often a byproduct of incorrect object design. When you compose an object of other objects, you force your development into building smaller objects with specific purposes.

    I find that thinking of objects in terms of “is a” and “has a” relationships: if your object is a configuration item, it extends; if your object has a configuration item, it is composed of configuration items.

  3. As far as I can tell, inheritance is really meant for a system where you want to extend pre-compiled code without having the source available. Sharing of implementation is simply a consequence of that feature, which is kind of evil. When you inherit, the behavior of your new class is defined in at least two places: child and parent. This can make the child’s behavior immensely difficult to understand, even if you do have the parent’s source code handy.

    In a dysfunctional language like Java, where all functions must be part of a class, a superclass is a convenient way to build a library of common functions used by the base classes to do their work. Without the concept of functions and either packages or namespaces, inheritance (and abstract classes) become the only tool for re-using common code while isolating it from the world at large.

  4. And by “build a library of common functions used by the base classes to do their work”, I mean “used by the subclasses”. D’oh.

  5. Can someone give an example of when aggregation should be preferred over inheritance? I can’t think of anything that isa and hasa could each apply to equally well.

    I tend to prefer inheritance whenever it reduces the amount of code I have to write. If I’m going to have to rewrite every public function with a new function that just calls through to the aggregate I feel like I’m following a policy to invent work for myself and introducing possible bugs along the way.

  6. @mccoyn: Think of writing an email class. Assume that Email is a class handles all the business of taking a message and delivering it. HtmlEmail would be a class that would inherit from the base Email class but contain extra functionality to format the headers appropriately.

    Now each of these classes should be able to support attachments. You wouldn’t want to have these classes extend EmailAttachment… Instead, the better solution is to compose each class with as many EmailAttachment instances as necessary.

    Cheers,
    Mike G.

  7. @Mike G: I think mccoyn meant an example where you would consider using inheritance from the outset, but would be better off using aggregation as it’s easier to maintain.

    I can’t imagine anyone actually considering that an Email class should extend an EmailAttachment class - unless they have no idea about hierarchy, in which case they’re probably at a far simpler level than those this tip is intended for.

  8. Yeah, for me I just use “isa” or “hasa” to decide. There really isn’t any preference for either model. I’ve seen the statement “prefer aggregation over inheritance,” argued in several places, but I don’t see them as the same thing. Either something “isa” and I use inheritance or it “hasa” and I use aggregation.

    Its like saying “prefer screwdrivers over hammers.”

    I’m just trying to gain some perspective because I’ve heard the argument multiple times.

  9. I think Bill Venners illustrates the issues quite well here:

    Composition versus Inheritance

    While you might disagree with the slightly contrived use of fruit, his conclusions sum up the advantages of both approaches. Note that these are typically “isa” situations - so you’d usually think inheritance, but composition and delegation may be a better way to go. “hasa” situations, on the other hand, will always require aggregation.

  10. Hmmm… my comment just got lost on post… Any chance of a preview button?

    Anyway, I think Bill Venners illustrates the issues quite well here:

    Composition versus Inheritance

    Contrived fruit examples aside, in “isa” situations you would usually think inheritance, but as the real world is often “sometimes-isa”, it may be that composition and delegation (of/to the “parent” class) will be more flexible in the long term - and it also may depend on where you have the most control (front-end/back-end).

    Inherent “hasa” situations can only be solved by aggregation and don’t enter into this issue.

  11. Testing… (None of my recent comments are showing up)

  12. Is there a problem with the link tags?

    Testing… Apple

  13. Nick - there seems to be a problem with comments - comment IDs 9123 and 9122 (I think) just seemed to disappear into the ether when I posted them - no error or anything.

    I’ll try again.

  14. Awesome, now I’m getting “Duplicate comment detected; it looks as though you’ve already said that!” - even though I can’t see the comments I made anywhere.

    Sorry for the comment spam y’all… I’ll try one more time.

  15. Any chance of a preview button?

    Anyway, I think Bill Venners illustrates the issues quite well here:

    Composition versus Inheritance

    Contrived fruit examples aside, in “isa” situations you would usually think inheritance, but as the real world is often “sometimes-isa”, it may be that composition and delegation (of/to the “parent” class) will be more flexible in the long term - and it also may depend on where you have the most control (front-end/back-end).

    Inherent “hasa” situations can only be solved by aggregation and don’t enter into this issue.

  16. Nope… still not working (comment #9129 got lost this time).

    I’ll try an abridged version:

    I think Bill Venners illustrates the issues quite well here:

    Composition versus Inheritance

    [snip]

  17. I think Bill Venners illustrates the point well here:

    http://www.artima.com/designtechniques/compoinh.html

    Contrived fruit examples aside, in “isa” situations you would usually think inheritance, but as the real world is often “sometimes-isa”, it may be that composition and delegation (of/to the “parent” class) will be more flexible in the long term - and it also may depend on where you have the most control (front-end/back-end).

    Inherent “hasa” situations can only be solved by aggregation and don’t enter into this issue.

  18. Composition versus Inheritance

  19. This is bizarre - I can’t post this link at all. Anyway… Google for “Composition versus Inheritance” and find the article by Bill Venners (on Artima and other places).

    Contrived fruit examples aside, in “isa” situations you would usually think inheritance, but as the real world is often “sometimes-isa”, it may be that composition and delegation (of/to the “parent” class) will be more flexible in the long term - and it also may depend on where you have the most control (front-end/back-end).

    Inherent “hasa” situations can only be solved by aggregation and don’t enter into this issue.

  20. Michael, my apologies for ‘akismet’ being totally crap and blocking nearly all of your comments, didnt even put them in ‘moderation’ just straight into spam..

  21. Bill Venners’ article describes preferring aggregation (which he calls composition) to improve encapsulation. When the interface of a superclass changes, it changes the interface of the subclass and so they are not isolated. Thus whenever the interface of a superclass changes you have to change the sub-class and the code that uses it. If you use aggregation, however, the changes stop at the sub-class implementation and the sub-class interface does not have to be changed.

    Its a good idea and certainly worth consideration whenever I create inheritance. The down side is that you have to write forwarder functions and you lose polymorphism. Bill Venner suggests that interfaces can be used effectively to provide polymorphism. I’ve never liked interfaces because they ofter force you to write trivial forwarder functions to reuse code. Perhaps something like Ruby’s concept of mixins could allow you to reuse blocks of forwarder code with interfaces.

    My interpretation then, is that you should prefer interfaces over inheritance for “isa” relationships and use aggregation to reuse code.

Post a Response