Java does not offer a clear mechanism for expressing code contracts. Only a few popular programming languages do, such as F#. The assert
keyword in Java is okay, but if you forget to enable it in the runtime, the contracts may not be checked in production. That is why many developers prefer to use (checked or unchecked) exceptions.
- If I am modeling the contracts of a library or utility class, I favor exceptions, following the wisdom of the most popular libraries.
- If I am modeling business classes and their interactions and I know that the data was cleaned up in previous layers (say, in the controller of a Model-View-Controller [MVC] architecture), I favor assertions. The data was already validated, and I am sure they start their work with valid data. I do not expect pre-conditions or post-conditions to be violated, so I prefer to use the
assert
instruction. It will throw anAssertionError
, which will halt execution. I also ensure that my final user does not see an exception stack trace but instead is shown a more elegant error page. - If I am modeling business classes but I am not sure whether the data was already cleaned up, I go for exceptions.
When it comes to validation, I tend not to use either assertions or exceptions. I prefer to model validations in more elegant ways. First, you rarely want to stop the validation when the first check fails. Instead, it is more common to show a complete list of errors to the user. Therefore, you need a structure that allows you to build the error message as you go. Second, you may want to model complex validations, which may require lots of code. Having all the validations in a single class or method may lead to code that is very long, highly complex, and hard to reuse.
If you are curious, I suggest the Specification pattern proposed by Eric Evans in his seminal Domain-Driven Design (2004). Another nice resource is the article “Use of Assertions” by John Regehr (2014); it discusses the pros and cons of assertions, misconceptions, and limitations in a very pragmatic way.
Finally, I used native Java exceptions, such as RuntimeException
. In practice, you may prefer to throw more specialized and semantic exceptions, such as NegativeValueException
. That helps clients treat business exceptions differently from real one-in-a-million exceptional behavior.
NOTE Formal semantics scholars do not favor the use of assertions over exceptions. I should not use the term design-by-contract for the snippets where I use an if
statement and throw an exception—that is defensive programming. But, as I said before, I am using the term design-by-contract for the idea of reflecting about contracts and somehow making them explicit in the code.
Leave a Reply