Don't let consistency rule your APIs
Consistency in API design is great. It makes the API more predictable, easier to learn, and more maintainable. But I believe there's a difference between being consistent when writing code, and writing code to be consistent.Consider this API:
public enum Status {
PENDING,
PREPARATION_IN_PROGRESS,
PREPARED,
DELIVERY_IN_PROGRESS,
DELIVERED;
CANCELLED,
public boolean isFinished() {
return this == CANCELLED || this == DELIVERED;
}
}
This API provides exactly what's necessary for the code that uses it. But to be consistent, I tend to extrapolate from
isFinished()
and add additional methods like isStarted()
and isActive()
. But these methods aren't needed! They're just dead code that needs to be written, documented, tested, and maintained only for the sake of consistency.Symmetry is a special case of extraneous consistency:
interface Stack<T> {
/**
* Adds {@code value} to the stack.
*/
void push(T value);
/**
* Removes and returns the most recently pushed value in the stack.
* @throws StackEmptyException if the stack contains no elements
*/
T pop();
}
Consistency tells me to create an exception type StackFullException
and to declare that push()
throws it when someone pushes more than Integer.MAX_VALUE
elements. This situation extremely rare! Writing the extra code and the tricky tests isn't worth the effort.Finally, a familiar example where consistency ruins an API:
public class InputStream {
int read() throws IOException;
int read(byte[] b) throws IOException;
void close() throws IOException;
}
When a call to read() throws an IOException
, the caller probably wants to recover from the error. But when close()
throws, there's not much the caller can do about it! As a result of this, I need nested try/catch blocks every time I close a stream: InputStream in = null;
try {
in = openInputStream();
return parse(in);
} catch(IOException e) {
throw new ApplicationException("Failed to read input", e);
} finally {
try {
if (in != null) {
in.close();
}
} catch(IOException ignored) {
}
}
A better, but less consistent, API might have close()
return a boolean to indicate whether the operation was successful.When writing APIs, be consistent and predictable. But don't be consistent just for consistency's sake - that could lead you to write too much or bad code.