API design mistakes will haunt you forever


The class org.apache.commons.net.ftp.FTPClient from the Apache Commons Net library is a good example of the use of inheritance where composition would have been more suitable.

The class builds on the low-level FTP support provided by its superclass, org.apache.commons.net.ftp.FTP. Its mission, as stated in the Javadocs, reads:

This class takes care of all low level details of interacting with an FTP server and provides a convenient higher level interface.

While that is certainly true, the use of inheritance makes the abstraction very leaky and confusing. When you autocomplete on an FTPClient object in your IDE, you will barely see all the methods starting with an "a", because all the low-level FTP methods are also included, plus the socket-layer level methods inherited from org.apache.commons.net.SocketClient.

This is a classic mistake, probably resulting from coders loving inheritance trees a little too much in the 1990s, before discovering that inheritance makes sense only for "is-a" relationships, while "uses-a" or "has-a" relationships are much more adequately expressed through composition.

If you're designing a library or framework for others to use, keep in mind that you will never ever be able to change anything that you have exposed as part of the public API. This is also the reason why many of Javas APIs seem a little dusty today: java.sql got the exception model totally wrong, and Java's date support was designed before the benefits of immutability were widely accepted. Now, there is no way out without breaking everybody's code.

This, obviously, also applies to some of the older Apache Commons libraries. The only thing you can do is provide a new, improved library, and wait until the old one falls into oblivion.