A Funny Java Flavoured Look at the World

Monday, October 16, 2006

Why you should use composition instead of inheritance

I have often read that you should favour composition (Interfaces) over using Abstract classes and inheritance. I have learnt myself that this is a better practise for creating reusable and easily maintainable code but I hadn't really come across a reason for this. So I started thinking about it over the weekend.

The one problem I can see with inheritance is that the code will filter down through many classes. Any changes to the Base Class will effect all the classes that extend the class. This might result in each class overriding the base methods. The more methods a base class the less cohesive the base class will be. It would be more beneficial to create individual objects (composition) so you can have cohesive objects doing one task. This would also increase the likelihood of you being able to use these objects in your code else where.

This in my experience is where using inheritance can get tricky. It starts off very well and cohesive but then making changes can effect a lot of code and the code starts to look like legacy code. What I am talking about is the coupling that is involved with inheritance.

I suppose in practise you could use inheritance effectively and most of use do in time to time but maintaining the code can become difficult with the more code you add the less cohesive. This also works against the idea of separating the code into small well focused objects. You should also try and split the code up into small sections, ideally behind a nice interface to reduce coupling and thus allow the ability to hide the effects of change in your code. If you have a number of small classes instead of using inheritance then you isolate the effects of change to one small class rather than changing the base class of an inheritance tree where the coupling is high.

6 Comments:

  • Inheritance doesn't necessarily mean behavioural inheritance.

    Inheriting method declarations from interfaces is still inheritance.

    Composition is simpler to test than behavioural inheritance.

    In Java, subclassing involves getting some behaviour from the superclass - at least the constructor. In other languages, at least C++, that doesn't have to be the case. So in Java, subclassing is not as simple as it could be.

    There's nothing wrong with implementing an interface (this is interface inheritance); the annoyance of implementing a wrapper class and having to provide delegation methods should not lead you to prefer subclassing, but to prefer smaller (simpler) interfaces, and/or the builder design pattern.

    It'd be interesting if you could programmatically replace a method definition with another, e.g., object.toString={object.x+","+object.y};

    This would also get us away from the delegation repetition problem.

    It can also be done in Java, kind of, if you use 'Function's instead of direct method calls. Functions would be objects that hold method implementations, rather like the Runnable interface, but more flexible:

    public class MyObject
    {
    public Function[MyObject,String] asString;

    public int x;
    public int y;
    }

    MyObject object=new MyObject();
    object.asString=new Function[MyObject,String]()
    {
    public String run(MyObject input)
    {
    return input.x+","+input.y;
    }
    };

    Or with closures: object.asString={object.x+","+object.y};

    Of course, you could also override toString() to return asString.run(this);

    Some utilities for functional programming in Java - http://code.google.com/p/functionalpeas/ (by me) and http://www.cs.chalmers.se/~bringert/hoj/ - Higher Order Java, unmaintained, but more complete than functionalpeas, albeit with some missing bits.

    By Blogger Ricky Clarkson, at Mon Oct 16, 02:05:00 pm 2006  

  • Inheritance is often awkward becuase you can only inherit from one class. If you want your object to implement a bunch of interfaces, all of which have default implementations that you want to reuse, then using inheritance doesn't work. In our case we often have between 10-15 interfaces that all our objects should implement, for example. You can use delegation, but as already pointed out, writing delegation wrappers is deadly boring and repetitive. With composition and delegation you also have a serious identity crisis: what is the "this" pointer? How do different composed objects find and use each other?

    We use AOP to solve all these problems. We have tons of interfaces which are implemented once, and these implementations are then introduced in base classes (which have no methods nor state, btw). Maximum reusability, no identity crisis (i.e. the "this" pointer works well), aspects can easily use each other (cast the "this" pointer to the interface you want and call it), etc. It's works really really well.


    Using AOP

    By Anonymous Anonymous, at Mon Oct 16, 03:11:00 pm 2006  

  • Hmmm… First, "yeah" to most of what Ricky Clarkson said (I didn't follow the bits at the end). Now my own comments.

    I don't understand the bit at the beginning about composition and interfaces. Composition is unrelated to the use of interfaces.

    I'm going to use the term "delegation" to be clear that I'm referring to the exposure of existing methods in another class as part of this class's public interface, rather than the private incidental usage of those methods within a particular implementation. Using inheritance for the latter has fallen quite out of favor. (As an Eiffel-ist I disagree, but that battle has been lost and anyway we're talking about Java which has no multiple inheritance.)

    The "fragile base class" problem isn't solved by using delegation instead. The fact is that whenever code is reused without copying it, changing that code can break any of the code that uses it. This is the driving force behind Bertrand Meyer's original meaning of the Open/Closed Principle: existing code is not to be changed except for bug fixes. Override the existing method with a wrapper or a replacement, but don't change the existing code.

    Creating subclasses through inheritance does not necessarily mean adding methods. It oftentimes means only the overriding of inherited methods. Even when methods are added, they do not affect the cohesiveness of the parent class and they can (and should) be totally cohesive for the subclass. The code is still split up into separate classes.

    Delegation does have advantages in certain cases. With inheritance, which class is the parent class cannot be changed short of recompiling. With inheritance, the client can always treat the object as an instance of the parent class. The GoF Proxy and Decorator patterns, for instance, are hobbled if you replace the delegation with inheritance. And since value types should block inheritance by being "final", delegation is the only way to reuse value classes.

    Another place where delegation can be easier to work with than inheritance is when the domain objects are persisted in a relational database. With delegation it's fairly straightforward to have each class in its own table in the database. With inheritance the O/R mapping can be more tricky. This is one of the reasons that Streamlined Modeling uses object inheritance which is based on delegation.

    Of course, one thing that delegation can't do is to implement the direct "is-a" relationship. Your object is not substitutable for an instance of the other class.

    By Blogger Doug, at Mon Oct 16, 05:49:00 pm 2006  

  • If you want to be taken seriously as a writer, please take some time to hone your writing skills. It is hard to take someone seriously when they do not understand basic grammar, composition and spelling. For example, your use of the term "effect" is wrong, and should instead be replaced with "affect." Additionally, stating that your only expertise in the subject you are about to talk about is based on hearsay and a weekend of "thinking about it" does not instill confidence in the reader. I am not trying to be an ass about this; I really think some more attention to detail can improve your writing and broaden your readership.

    As for inheritance versus composition, I stick to the traditional is/a or has/a test. If you have a "Vehicle" class and a "Car" class, the car class should use inheritance from the vehicle class because the car IS A vehicle. If you have a "Wheel" class and a "Car" class, you should use composition, because a car HAS A wheel. I know this seems overly simplistic, but I have always believed simplicity in code saves many headaches over using "cleverly" complex code schemes.

    By Anonymous Anonymous, at Tue Oct 17, 07:32:00 am 2006  

  • I extracted this problem to an antipattern of a deeper class hierarchy.

    By Anonymous Anonymous, at Tue Oct 17, 07:35:00 am 2006  

  • By Anonymous Anonymous, at Tue Oct 17, 05:06:00 pm 2006  

Post a Comment

<< Home