I like simple rules of thumb that will help developers create better, more adaptable designs. So here’s another: Never throw an exception. And if someone throws one at you, catch it immediately and don’t pass it on.
Why? Aren’t exceptions one of the mainstays of modern programming? Joel Spolsky outlines the basic arguments against throwing exceptions:
“…I consider exceptions to be no better than “goto’s”, considered harmful since the 1960s, in that they create an abrupt jump from one point of code to another. In fact they are significantly worse than goto’s…”
But then he goes on to give advice that I consider questionable at best, by suggesting that you catch exceptions quickly and convert them into return codes. I agree that they should be caught at the earliest possible moment, but I disagree with converting them into return codes. Because although that certainly makes the mechanism visible to the reader of the code, it still represents strong coupling where there should be none. The caller – or someone further back along the call stack – has to know about, and take action in response to, someone else’s problem. It seems to me that such coupling violates the pragmatic programmers’ tell don’t ask principle.
So what can we do when someone throws an exception at us? To avoid strong coupling between parts of the system we shouldn’t inform the caller that we had a problem. Instead, the object that catches the exception should set its own state such that it will answer future messages in the light of the problem that occurred. (In particular, the NullObject or SpecialCase pattern is useful here.)
For example, suppose we have a system that uses an external search engine, displaying the results on a GUI. And suppose that the API to the search engine throws an exception if the engine cannot be contacted – for example if the network connection is not available. If this occurs, we wish the GUI to display an error message instead of the search results. The above argument states that we shouldn’t put a
try-catch block in the GUI, with the catch part containing the code to display the error. Instead, the
SearchResult object returned by the engine could be special, having internal state that accords with the failure. Further, the tell-don’t-ask rule tells us we still shouldn’t query the
SearchResult‘s state in order to discover what to display. Instead the GUI could tell the object to display itself. Then a normal
SearchResult object would display a table of results, while a special
SearchResult object would display an appropriate error message.
Finally, note that the above also has implications for hexagonal architectures. The only code in our system that is permitted to throw exceptions is now the APIs we use. And the adapters that connect us to those APIs must catch any exceptions immediately, before they can propagate up into the inner hexagon where the domain and applicationm objects live. The presence of try-catch blocks is therefore yet another difference in code style between the inner and outer hexagons.