5/16/08

Choose composition over inheritance

One particular aspect of how to structure your code in order to make it more testable is the way we build complex objects out of smaller islands of functionality. In object-oriented programming languages, the main candidates for doing that are inheritance and composition.

Inheritance, having a class inherit functionality from a superclass, has traditionally been used to enable reuse of functionality within a class hierarchy. Although it's a nice feature for a programming language, inheritance does have its downside when it comes to testability, maintainability, and overall flexibility of design.

Specifically, having to deal with an inheritance hierarchy can sometimes make it unnecessarily difficult to instantiate our objects in a test harness. In Java, for example, we might have to provide valid input only needed by the superclass constructor even though we're just interested in an aspect of the child class. This can be a real pain if the input itself is a complex object groph and, thus, difficult or laborious to instantiate.

Furthermore, even the smallest of changes could potentially cause a ripple effect throughout the class hierarchy, which is obviously not an idea situation. Inheritance is often too restrictive to be easily testable construct. This leads us to the alternative -- composition.

Composition is a way to build objects that provide complex functionality by combining a set of less complexity component objects. The top-level composite object delegates work to its components instead of invoking methods from its superclass. In essence, composition is based on object-level division of responsibility instead of static, class-level division. Composition tends to be slightly more verbose than inheritance, measured in lines of code, but its improved characteristics in terms of testability, flexibility, and maintainability often more than outweigh the cost of those extra lines of code.