Separate APIs for Clients and Providers

From APIDesign

Revision as of 19:39, 17 August 2008 by JaroslavTulach (Talk | contribs)
Jump to: navigation, search

Have You Ever Wondered...?

Have you ever subclassed some API class and started to ask yourself: "Shall I override its particular method or just call it?" Or: "Is this method public because external users shall call it or am I supposed to override it?". Chapter 8 brings you answer to such common worries. It analyses the differences between evolution of APIs targeted to different clients, analyses what kind of messages various Java access modifiers carry and gives a recipe to convert a class full of methods with unclear multiple meanings into an API that carries just one, clear message for every developer using it.

The Writer

Significant part of this chapter builds a showcase around the Writer example. Do you know that in JDK 1.5 new methods has been added into the Writer class to work with CharSequences? Yes, it was. Could it be left abstract? No, that would not be compatible! It needed some implementation. Some, but which one? There seems to be multiple implementation choices to use. Now a small quiz: "which one would you choose?"

Code from AltBufferedWriter.java:
See the whole file.

public Writer append(CharSequence csq) throws IOException {
    /* thrown an exception as this method is new and 
     subclasses need to override it */
    throw new UnsupportedOperationException();
}
 

Possible, but useless, for clients calling the Writer.append method. They would always have to code very defensively, as shown here:

Code from BufferedWriterThrowingExceptionTest.java:
See the whole file.

try {
    bufferedWriter.append(what);
} catch (UnsupportedOperationException ex) {
    bufferedWriter.write(what.toString());
}
 

Of course, this is a very horrible API. You want to guarantee to clients calling into Writer that reasonable implementation of the append method is provided:

Code from AltBufferedWriter.java:
See the whole file.

if (csq == null) {
    write("null");
} else {
    write(csq.toString());
}
return this;
 

This looks good, as clients can always rely on the append method to be implemented. Even by writers written against JDK 1.4 version of the Writer. This is not bad, however it is also not very efficient, as it always converts the whole CharSequence to String. In case of big sequences, like onces representing whole CD, this is a way to ask for OutOfMemoryErrors:

Code from BufferedWriterOnCDImageTest.java:
See the whole file.

CountingWriter writer = new CountingWriter();
CDSequence cdImage = new CDSequence();
BufferedWriter bufferedWriter = new BufferedWriter(writer);
bufferedWriter.append(cdImage);
assertEquals(
    "Correct number of writes delegated", 
    cdImage.length(), writer.getCharacterCount()
);
 

There must be more effective way to do this! Yes, it is. Please read the chapter 8 of TheAPIBook to learn what to do and how to prevent situations like this.

Personal tools
buy