<<Clean Code>> Quotes: 7. Error Handling
Things can go wrong, and when they do, we as programmers are responsible for making sure that our code does what it needs to do.
Error handling is important, but if it obscures logic, it’s wrong.
Use Exceptions Rather Than Return Codes
You either set an error flag or returned an error code that the caller could check.
The problem with these approaches is that they clutter the caller. The caller must check for errors immediately after the call. Unfortunately, it’s easy to forget.
Write Your Try-Catch-Finally Statement First
One of the most interesting things about exceptions is that they define a scope within your program.
Your catch has to leave your program in a consistent state, no matter why happens in the try. For this reason it is good practice to start with a try-catch-finally statement when you are writing code that could throw exceptions. The helps you define what the user of that code should expect, no matter what goes wrong with the code that is executed in the try.
Try to write tests that force exceptions, and then add behavior to your handler to satisfy your tests
Use Unchecked Exceptions
The signature of every method would list all of the exceptions that it could pass to its caller. Moreover, these exceptions were part of the type of the method.
What price? The price of checked exceptions is an Open/Closed Principle violation. If you throw a checked exception from a method in your code and the catch is three levels above, you must declare that exception in the signature of each method between you and the catch. This means that a change at a low level of the software can force signature changes on many higher levels.
Encapsulation is broken because all functions in the path of a throw must know about details of that low-level exception.
Provide Context with Exceptions
In Java, you can get a stack trace from any exception; however, a stack trace can’t tell you the intent of the operation that failed.
Create informative error messages and pass them along with your exceptions. Mention the operation that failed and the type of failure.
Define Exception Classes in Terns of a Caller’s Needs
However, when we define exception classes in an application, our most important concern should be how they are caught.
In fact, wrapping third-party APIs is a best practice, When you wrap a third-party API, you minimize your dependencies upon it: You can choose to move to a different library in the future without much penalty. Wrapping also makes it easier to mock out third-party calls when you are testing your own code.
One final advantage of wrapping is that you aren’t tied to a particular vendor’s API design choices.
Define the Normal Flow
This is called the SPECIAL CASE PATTERN. You create a class or configure an object so that it handles a special case for you . When you do, the client code doesn’t have to deal with exceptional behavior, That behavior is encapsulated in the special case object.
Don’t Return Null
I think that any discussion about erro handling should include mention of the things we do that invite errors. The first on the list is returning null.
When we return null, we are essentially creating work for ourselves and foisting problems upon our callers.
It’s easy to say that the problem with the code above is that it is missing a null check, but in actuality, the problem is that it has too many. If you are tempted to return null from a method, consider throwing an exception or returning a SPECIAL CASE object instead. If you are calling a null-returning method from a third-party API, consider wrapping that method with a method that either throws an exception or returns a special case object.
Don’t Pass Null
Returning null from method is bad, but passing null into methods is worse.
In most programming languages there is no good way to deal with a null that is passed by a caller accidentally.
Conclusion
Clean code is readable, but it must also be robust.