Re: Exception Handling
Novice wrote:
Patricia Shanahan :
Novice wrote:
Lew wrote:
...
It is common and frequently useful to create an application-specific
checked exception. Again, and I've said this many times, think about
what will be useful when troubleshooting a problem. Many times, a
custom exception is useful. It says that the underlying exception has
been caught, logged and wrapped.
But how is a custom FooException better than a good ol'
IllegalArgumentException? I'm not quite getting that yet.
Generally, you should use different Exception subclasses for cases that
may need different handling, so that callers can set up appropriate
catch blocks.
Regardless of the conditions under which you choose to generate
IllegalArgumentException, many methods you will be calling use it to
indicate an illegal argument condition that has not yet been logged or
otherwise reported.
If you also use it to indicate exceptions that have already been
logged,
you risk either repeated logging of the same issue, or failure to log.
On the other hand, if you create an Exception subclass that you only
throw after logging the underlying problem, there is no ambiguity.
Callers have to deal with the consequences of the failure, but should
only generate log messages if they have something to add. The
underlying
problem has already been logged.
I'm sure this will seem like good advice when I understand it but it's
still vague to me.
It would really help if you could suggest a concrete example or two that
show when to use a custom exception and why it's better.
This is treading into the matter of art. I can show the idiom, but no way can
I generate an entire project for you to show the impact of the logging
strategy across the project, which is the point of the idiom.
Your entire application has layers. The deeper low-level layers are most
likely to encounter problems first. Runtime exceptions are indicative of
programming errors, but they're slippery, too. You don't have to catch them,
which means an unguarded runtime exception can bring your system crashing
down. That's not something you want to design it to do.
A low-level layer by definition brings some model of the world into
conformance with your application's model. It might read a file and extract
data to objects, or persist objects to a database, or transform your
application's model into and out of XML documents for communication to other
modules. Whatever the low layer does, the higher-level client code deals with
abstractions relevant to the program. It's no longer a database record, for
example, it's a 'Customer' with related 'Order' elements in a collection.
If something goes wrong at a deep layer, the higher-level client must first,
become aware of it, and second, have some way to handle it. At that level,
"runtime exception" or 'IOException' are too low-level - they aren't in the
semantics of the application model. So the lower level, in addition to
transforming extrinsic models of good stuff into the application domain, also
must transform bad stuff like exceptions.
One good way to do this is to have an application-specific checked exception
like 'FooException' for the "Foo" application. As Patricia says, this marks
for higher-level code that lower-level stuff has done its job. At the higher
level, we care only that the details are packaged in the exception's 'cause',
not what the details are. A 'FooException', being checked, must be caught, so
it cannot accidentally crash the application. Since all higher-level code sees
'FooException' as a marker for bad stuff, and a black box, they all do the
same thing with it, more or less. They return the program to valid state
"state" - definable by a quick Google search, for Pete's sake! Come on, man!
Do a /little/ of the lifting yourself. And no, this isn't "bludgeoning". You
get your ego in the way and you won't learn. There's a reason we expect you to
act like a professional.
such as a decision screen or restart position, perhaps extracting standardized
fields from the 'FooException' for the error message.
Much of programming only makes sense if you think architecturally - that is,
holistically. Each layer has its own job, and its own vocabulary for that job.
"Foo" has no business knowing about "I/O" in its Foo-specific logic, and no
business knowing about "Foo" behavior in its I/O logic.
Separation of concerns.
Law of Demeter.
Object oriented.
Modularity.
--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg