17

I always find myself wrestling with this... trying to find the right balance between try/catching and the code not becoming this obscene mess of tabs, brackets, and exceptions being thrown back up the call stack like a hot potato.  For example, I have an app I'm developing right now that uses SQLite.  I have a Database interface that abstracts the SQLite calls, and a Model that accepts things to go in/out of the Database...  So if/when an SQLite exception occurs, it has to get tossed up to the Model (whom called it), who has to pass it off to whoever called the AddRecord/DeleteRecord/whatever...  

I'm a fan of exceptions as opposed to returning error codes because error codes can be ignored, forgotten, etc., whereas an Exception essentially has to be handled (granted, I could catch and move on immediately...)   I'm certain there's got to be a better way than what I've got going on right now.

Edit:  I should have phrased this a little differently.  I understand to re-throw as different types and such, I worded that poorly and that's my own fault.   My question is...   how does one best keep the code clean when doing so?   It just starts to feel extremely cluttered to me after a while.

3
  • Which programming language?
    – Apalala
    Mar 31, 2011 at 22:31
  • 2
    C# at the moment, but I'm trying to think in general.
    – trycatch
    Mar 31, 2011 at 22:32
  • 1
    C# doesn't force declaration of exceptions thrown, which makes it easier to handle exceptions where it's reasonable, and avoid programmers being tempted to catch them without actually handling them. Anders Hejlsberg, designer of C#, makes the case against checked exceptions in this article artima.com/intv/handcuffs.html
    – Apalala
    Mar 31, 2011 at 22:47

4 Answers 4

16

Think of it in terms of strong typing, even if you aren't using a strongly typed language - if your method can't return the type you expected it to, it should be throwing an exception.

Also, rather than throwing the SQLException all the way to the model (or worse, UI), each layer should catch known exceptions and wrap/mutate/replace them with exceptions suited to that layer:

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

This should help limit the number of exceptions you are looking for in each layer and help you maintain an organized exception system.

6
  • I made a few edits, worded the original Q poorly.. my question is more about how to keep the code clean while handling all the trying and catching and whatnot. It starts to feel very messy when try catch blocks are everywhere...
    – trycatch
    Mar 31, 2011 at 22:50
  • 1
    @Ed doesn't it bother you that your UI or Model is catching "SQLException"? To me, that's not very relevant. To each their own, I guess.
    – Nicole
    Mar 31, 2011 at 23:01
  • 1
    @Ed, that might be sort of OK in languages that don't have checked exceptions, but in languages with checked exceptions it's really ugly to have throws SQLException on a method that doesn't imply that SQL is even involved. And what happens if you decide that some operations should go to a file store? Now you have to declare throws SQLException, IOException, etc. It'll get out of hand. Mar 31, 2011 at 23:23
  • 1
    @Ed to the end user SQLException does not mean much, especially so if your system can support multiple types of persistence apart from relational databases. As a gui programmer I'd much rather have to deal with DataNotFoundException than a whole set of low level exceptions. Very often an exception to a low level library is just part of normal life up above, in this case they are much easier to handle when their level of abstraction match the application's.
    – Newtopian
    Apr 1, 2011 at 2:35
  • 1
    @Newtopian - I never said present the raw Exception to the end user. To end users, a simple 'Program has stopped working' message is enough whilst logging the Exception somewhere useful for support to pick up. In Debug mode it is useful to display the Exception. You shouldn't be caring about every possible Exception an API could throw unless you have a specific handler for such Exceptions; just let your Un-handled Exception catcher deal with it all.
    – Ed James
    Apr 1, 2011 at 8:06
10

Exceptions allow for writing cleaner code because the bulk of it takes care of the normal case, and the exceptional cases can be handled later on, even on a different context.

The rule for handling (catching) exceptions is that it must be done by context that can actually do something about it. But that has an exeption:

Exceptions must be caught at module boundaries (specially layer boundaries) and even if only to enclose them an throw a higher level exception that has meaning to the caller. Each module and layer must hide its implementation details even regarding exceptions (a heap module may throw HeapFull but never ArrayIndexOutOfBounds).

In your example, it is unlikely that upper layers can do anything about an SQLite exception (if they do, then it is all so coupled to SQLite that you won't be able to switch the data layer to something else). There are a handful of foreseeable reasons for things like Add/Delete/Update to fail, and some of them (incompatible changes in concurrent transactions) are impossible to recover from even in the data/persistence layer (violation of integrity rules, f.i.). The persistence layer should translate the exceptions to something meaningful in the model layer terms so upper layers can decide if to retry or to fail gracefully.

4
  • I agree, and edited my question to reflect that where I had worded it badly. I meant for this to be more a question of how to keep the trying and catching from cluttering things up.
    – trycatch
    Mar 31, 2011 at 22:52
  • You can apply the principles @Ed James and I mentioned within a layer or module. Instead of calling SQLite directly from all over the place, have a few methods/functions that talk to SQLite and either recover from the exceptions, or translate them to more generic ones. If a transaction involves several queries and an updates, you don't need to handle every possible exception in each: a single outer try-catch can translate the exceptions, and an inner one can take care of partial updates with a rollback. You can also move the updates to their own function for simpler exception handling.
    – Apalala
    Mar 31, 2011 at 23:03
  • 1
    this stuff would be easier to understand with code examples.. Mar 31, 2011 at 23:30
  • This was probably a question better suited to stackoverflow from the start.
    – Apalala
    Apr 1, 2011 at 14:03
5

As a general rule you should only catch specific Exceptions (e.g. IOException), and only if you have something specific to do once you've caught the Exception.

Otherwise, its often best to let Exceptions bubble up to the surface so they can be exposed and dealt with. Some folks call this fail-fast.

You should have some kind of handler at the root of your application to catch un-handled Exceptions that have bubbled up from below. This affords you the chance to present, report or manage the Exception in a suitable way.

Wrapping Exceptions is useful when you need to throw an Exception across a distributed system and the client doesn't have the definition of the server-side fault.

6
  • Catching and silencing exceptions is a terrible thing to do. If the program's broken, it should crash with an exception and a traceback. It should muddle along silently logging things but making a mess of the data.
    – S.Lott
    Mar 31, 2011 at 23:58
  • 1
    @S.Lott I agree that one should not silence exception because they find them annoying but just crashing an application is a bit extreme. There are many cases where it is possible to catch the exception and reset the system in a known state, in such cases the global handler of everything is quite useful. If, however the reset process fails in turn in a way that cannot be safely dealt with then yes, let it crash is far better than sticking one's head in the sand.
    – Newtopian
    Apr 1, 2011 at 2:39
  • 2
    again it all depends on what you are building but I generally disagree with just letting them bubble up as it creates abstraction leakage. When I use an API I'd much rather see it exposes exceptions that are in tune with the API's model. I also hate surprises so I hate when an API just let exception related to it's implementation peek through unannounced. If I have no idea what's coming at me how can I possibly react ! I find myself too often use much wider catch nets to prevent surprises from crashing my app unannounced.
    – Newtopian
    Apr 1, 2011 at 2:44
  • @Newtopian: "Wrapping" exceptions and "Rewriting" reduce leakage of abstractions. They still bubble up appropriately. "If I have no idea what's coming at me how can I possibly react ! I find myself too often use much wider catch nets" Is the thing we're suggesting you stop doing. You don't need to catch everything. 80% of the time, the right thing is to catch nothing. 20% of the time there's a meaningful response.
    – S.Lott
    Apr 1, 2011 at 9:47
  • 1
    @Newtopian, I think we need to distinguish between exceptions which will normally be thrown by an object and thus should be wrapped, and exceptions which arise due to bugs in the object's code and shouldn't be wrapped. Apr 1, 2011 at 12:45
4

Imagine that you are writing a stack class. You don't put any exception handling code in the class, as a result it could produce the following exceptions.

  1. ArrayIndexError - raised when the user attempts to pop from empty stack
  2. NullPtrException - raised due to a bug in the implementation causing an attempt to reference a null reference

A simplistic approach to wrapping exceptions might decide to wrap both of these exceptions in a StackError exception class. However, this really misses the point of wrapping exceptions. If an object throws a low-level exception that should mean the object is broken. However, there is one case where this is acceptable: when the object is in fact broken.

The point of wrapping exceptions is that the object should give proper exceptions for normal errors. The stack should raise StackEmpty not ArrayIndexError when popping from an empty stack. The intent is not to avoid throwing other exceptions if the object or code is broken.

What we really want to avoid is catching low-level exceptions which have been passed through high-level objects. A stack class which throws an ArrayIndexError when popping from an empty stack is a minor issue. If you actually catch that ArrayIndexError then we have a serious problem. Propogation of low-level errors is a far less serious sin then catching them.

To bring this back to your example of a SQLException: why are you getting SQLExceptions? One reason is because you are passing invalid queries. However, if your data access layer is generating bad queries, its broken. It shouldn't attempt to rewrap its brokenness in a DataAccessFailure exception.

However, a SQLException could also arise due to a loss of connection to the database. My strategy on that point is to catch the exception at the last line of defense, report to the user that database connectivity was lost and shutdown. Since the application has loss access to the database, there is really not much more that can be done.

I don't know what your code looks like. But it sounds like you might be blindly translating all exceptions into higher level exceptions. You should only be doing that in a relatively small number of cases. Most lower level exceptions indicate bugs in your code. Catching and rewrapping them is counter-productive.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.