Through my work, I often run across an API pattern that looks like this: it consists of the API definition itself and some definition of how to consume the API. An example:
Three typical options
If you look at the get method in this API, one typically has three options on defining it’s behaviour (or contract):
- return a BillingPeriod object
- return null if there’s nothing to return
- throw an exception if there’s nothing to return
From what I’ve seen, returning null is common as well as throwing an exception. Every now and then (way too often according to the bug tracker) the usage of API on either of these patterns fail – the consumer of simply forgets to check with the boolean method before calling the getCurrentOrNextPeriod().
I really really really think that putting “null” in source code should be frowned upon and only used in very rare circumstances. The problem with returning null is that you have to check if you received a null reference instead of a reference pointing to an actual object. Throwing an exception may be a “safer” way saying “HEY you really shouldn’t proceed now since you asked me to get something that I can’t provide, and you shouldn’t have asked me for stuff I don’t have in the first place and so you probably may do other harmful stuff if I don’t interrupt you now right away”.
A fourth option
A fourth option would be to return a Nullable – a pattern suggested by Uncle Bob and others. This is simply a reference that you can ask: “do you point to an actual object, or will you give me a null reference back?”
which would let us rewrite the API to:
But, while this surely adds information saying “there may not be a BillingPeriod – you should check first”, it still relies on the programmer to call the
hasValue() method before
Wait, there’s a fifth option!
But, there’s also a fifth option. With a more “fluent” API one may enforce a completely fail safe usage pattern:
which would make a consumer look like this:
This would force the consuming programmer to use the API properly and in a safe way.
Yes, it requires some more code from the programmer that provides the API, but it’s not that much and it does reduce errors in the consuming programmer’s code! And bugs that are discovered “late” (when the API is used (in production)) are always much more expensive than bugs discovered “early” (when the API is produced).