Design patterns - Exceptions patterns
In this chapter, we'll discuss behavioral design patterns involving Exceptions. Most of the chapter is based on the so called fault barrier pattern, which is described in various articles, for instance here(Oracle.com).
In order to understand the lecture it is important that you are up to par on exceptions in Java and understand the concepts of
- Runtime exceptions vs checked exceptions
- Exception chaining (wrapping an exception inside another one)
- The syntax rules
- Handle or declare
- Catching an exception
- Throwing an exception
If you feel that you have to freshen up on exceptions, please revisit the chapter on exceptions in our book Programming with Java. The chapter has video lectures, slides and exercises on exceptions. After reading it, return here and see the video for this chapter, where we talk more about the fault barrier idea and related stuff.
In short, the fault barrier pattern is a suggested way to think about exceptions consistently throughout an application. The first thing to do is to think about exceptions as two major groups:
- Faults (unrecoverable bad things that should never happen)
- Contingencies (things we'd expect to happen - but not very often - that we can recover from)
It is important that the project members share a view on this division and that developers talk to each other. The pattern tries to solve a situation where developers don't communicate about exceptions they must handle-or-declare, and rather hide them and pretend it's business as usual. With a consistent view on how to deal with exceptions, and good communication between team members, chances are great that the application becomes more robust and easier to maintain.
Faults are events which shouldn't happen because they are critical to the operations of the application. When they do happen anyway, and there is no way to recover from them, they should be propagated up the call chain to a level close to the main method (or whatever entry point your application has) and dealt with by a so called fault barrier.
All faults should be represented as runtime exceptions, so that
- They will propagate up the call chain (no one before the fault barrier is allowed to catch them)
- Developers don't have to focus on them being thrown from code down the call stack (methods they call etc)
Runtime exceptions are very suitable for this. A method which throws a RuntimeException of some sort, doesn't have to declare that it does so (but of course should document it). Methods calling such a method isn't required to handle any
RuntimeExceptions either. This lets the programmers to focus on contingencies, which are events that rather represent alternative outcomes of method calls - situations which are not the typical result of a method call, but plausible.
Checked exceptions are very suitable for contingencies, as it turns out. Contingencies are stuff we plan for could happen, even though they might happen rarely. And we should alert the code who cares about the contingencies when they do happen (since we planned for it). Making contingencies checked exceptions, will actually get the attention of the programmer responsible for getting the message.
Finally, we talk a little in the video lecture about hiding low-level stuff from high-level code. As an example, we take a call to a statistics module from a GUI (graphical user interface). If the statistics module is using database calls to gather the stats, that's none of the GUI code's business. The statistics module should never leak information about this low-level decision, because that would in the case of throwing a checked SQLException force the GUI code to import the exception from the java.sql package. When the statistics module changes and starts to use some webservice to gather the stats, then the SQLException will probably not be thrown any more, and the GUI code needs to be changed (and re-compiled).
Instead, we'd create our own StatisticsException for instance and make it part of the same package as the statistics classes. This way, the calling code (from e.g. the GUI) will never have to change, and is only forced to import code from one package in order to use the statistics module.
If the statistics module runs into a database related exception, it should wrap it inside one of its own exceptions and throw that instead.
Should we make the StatisticsException a checked or unchecked exception? It depends on whether it's a fault or contingency, of course!
- Coming soon - see the exceptions chapter in Programming with Java for now
Books this chapter is a part of
- Programming with Java - Exceptions chapter
- Effective exceptions (Oracle)
- Oracle tutorial on Exceptions
- Artima - Failure and Exceptions - A Conversation with James Gosling, Part II
- blog post on exceptions