Re: Exception handling
Andre Rothe wrote:
Hello!
I try to find some information about a best-practice exception
framework. Maybe this group can help me.
An application throws an exception within a low-level method, let us
say, within a database access layer. It has occured as reaction of a
wrong value given by the user. The method can throw the exception, the
next higher abstraction layer can rollback the associated database
operation and throw it up to the client application.
This is a bad pattern.
The low-level methods should through exceptions which indicate
programmer error. Higher-level methods should validate *before* calling
lower-level methods. The high-level methods can either throw
appropriate exceptions or directly report problems to the user.
The reason I advocate this pattern is because Low-Level methods
shouldn't know or care whether their invocation is due to a user action,
or an automated process, or something else.
Low-Level methods should be designed to report programmer errors and
low-level interface problems (IO errors, DB errors, etc...)
Higher level methods should be task oriented (for example, "User
requested a record") The higher level method should verify that the
user entered valid data, and that the record was returned correctly.
It may seem like a little bit of repeating yourself, but once you
realize that the lower-level exception has a different purpose, you can
get over the fact that you might write the same check in two places.
To avoid the duplication of "checks", you can create a predicate method
somewhere that can be called by the lower-level method and the higher
level method. In the lower-level method, you can document in the
contract that the precondition is "isValidFoo()" (for example). This
makes it a programmer error to call a method with a bad precondition.
Now, if the lower-level method throws an exception, it can be of three
kinds: High-Level Programmer error (bug: failed to verify
precondition), Low-Level Programmer Error (bug: in the library),
Exceptional Circumstance (i.e. IOException). The first two should be
reported to the user only as "There was an internal error handling your
request", and the full stack-trace logged somewhere for reporting and
reproducing. The latter exception can be reported to the user with an
appropriate message (eg. "Unable to communicate with database", or
"Unable to open file")
Of course, this is all idealized. Sometimes it is infeasible to verify
user-input before calling the lower-level method. In which case, the
lower-level method can still throw a "low-level" exception, but it
should still be translated into "high-level" message by the higher level
code.
I hope this all makes sense. I could go on further, but if I go on any
longer and I'll have to break it into sections, gather references, add
markup, throw and catch exceptions, etc... ;-)