Comparing composition and inheritance
So how exactly do composition and inheritance compare? Here are several
points of comparison:
It is easier to change the interface of a back-end class
(composition) than a superclass (inheritance). As the previous example
illustrated, a change to the interface of a back-end class necessitates
a change to the front-end class implementation, but not necessarily the
front-end interface. Code that depends only on the front-end interface
still works, so long as the front-end interface remains the same. By
contrast, a change to a superclass's interface can not only ripple down
the inheritance hierarchy to subclasses, but can also ripple out to
code that uses just the subclass's interface.
It is easier to change the interface of a front-end class
(composition) than a subclass (inheritance). Just as superclasses can
be fragile, subclasses can be rigid. You can't just change a
subclass's interface without making sure the subclass's new interface
is compatible with that of its supertypes. For example, you can't add
to a subclass a method with the same signature but a different return
type as a method inherited from a superclass. Composition, on the
other hand, allows you to change the interface of a front-end class
without affecting back-end classes.
Composition allows you to delay the creation of back-end objects
until (and unless) they are needed, as well as changing the back-end
objects dynamically throughout the lifetime of the front-end object.
With inheritance, you get the image of the superclass in your subclass
object image as soon as the subclass is created, and it remains part of
the subclass object throughout the lifetime of the subclass.
It is easier to add new subclasses (inheritance) than it is to add
new front-end classes (composition), because inheritance comes with
polymorphism. If you have a bit of code that relies only on a
superclass interface, that code can work with a new subclass without
change. This is not true of composition, unless you use composition
with interfaces. Used together, composition and interfaces make a very
powerful design tool. I'll talk about this approach in next month's
Design Techniques article.
The explicit method-invocation forwarding (or delegation) approach
of composition will often have a performance cost as compared to
inheritance's single invocation of an inherited superclass method
implementation. I say "often" here because the performance really
depends on many factors, including how the JVM optimizes the program as
it executes it.
With both composition and inheritance, changing the implementation
(not the interface) of any class is easy. The ripple effect of
implementation changes remain inside the same class.