Friday 23 September 2011

In The Red Corner Inheritance, In The Blue Corner Composition

The Problem
I’m currently working on a development project and see a couple of mistakes that seem to be repeated over and over. One of these mistakes is the over use of inheritance, where composition should be used instead.

These problems usually start with one developer writing a subclass to perform a function. Another developer will then come along and decide that another function is required in the hierarchy, and before you know it you have an explosion of subclasses. Some of the problems of hierarchies grown like this are that no-one really knows what each class does, there are classes for each permutation of responsibilities and the concepts behind the original classes are eroded to the point where they bear no resemblance to the software that is actually in the repository.

An Example
We are currently working with a component model, where each component has a set of ports. These ports are primarily a way of identifying a channel for messages to be sent from one component to another.


However while building the test harness it became clear that it would be very useful to capture the messages that are being sent over this channel, so a new type of port was born, the MessageCollectingPort. The MessageCollectingPort was only used in test code, and was substituted into the component graph where it was deemed necessary to capture the messages.


As the design progressed, another developer on the team decided that certain domain functionality was most easily placed in the port as well, leading to a DomainPort, and of course people wanted to capture the messages sent through domain ports.


The First Problem
Now you can start to see some of the problems with this “minor alteration” to the hierarchy. There are three responsibilities in the hierarchy:
  • Message delivery 
  • Message capture 
  • Message manipulation
The consequence of this is that approach is that for every responsibility the hierarchy doubles the size of the hierarchy. This means that for a hierarchy of five responsibilities would have sixteen classes if all permutations were realised. Anyone that has been in the position of trying to understand this number of classes will know that it is not easy...

The Second Problem
A further problem with this approach is that if we want to decouple our logic from the hierarchy, and not use polymorphism, it causes us to write multiple conditions in our code:

if(object instanceof MessageCollectingPort)

becomes:

if(object instanceof MessageCollectingPort || object instanceof MessageCollectingDomainPort)

This means that multiple places in the code need to be updated whenever a new class is added to the hierarchy. This violates the principle of locality, which states that the changes required should be close, in the developers mind, to change that caused them. This principle is core to writing maintainable software.

The Solution
Now that we have a grasp of the problems involved, we can start to look at solutions.

Ideally we want to add one new class for each new responsibility rather than two to the power of the number of responsibilities.

A powerful solution for dealing with this problem is to change from using inheritance to using composition, by using a decorator pattern.


See the decorator pattern in the ‘Gang Of Four’ for further details. This solution fulfils our ideal design criteria of one class per responsibility and means that we have a single criterion to check.

It still is not ideal, as we have to loop over the IPort instances in detect capabilities, or find some other method, such as using an object to represent the capability rather than using an instanceof check. It is however a significant improvement over the original.

The Behaviours
So what sort of thought processes, lead to the behaviours that generate this? Typically the justification that you get is “it was already done like this”. This indicates that the developer has not stood back and thought about the consequences of the design. This forethought can be encouraged by the team leader or design authority asking questions of the design before it starts.

No comments:

Post a Comment