4 ms·
Can someone who uses exceptions in large systems offer a comment on which exceptions they throw? One of the problems I have in trying to design with exceptions
by jbert 14y ago
Can someone who uses exceptions in large systems offer a comment on which exceptions they throw?
One of the problems I have in trying to design with exceptions is that they seem to offer a lot of potential to break abstraction layers.
As a concrete example, if you have a cacheing layer which - say - is implemented over the filesystem. In the event of an inability to access the correct location, the low-level routines might throw a permission-related exception. If you swap out the filesystem cacheing implementation with, say, one based on memcached you might instead get a network-related exception.
Neither of these types really make sense in the context of a 'cacheing layer error', they seem to leak implementation details - breaking the abstraction.
Do people accept this, or do they catch-and-rethrow new exception types at major layer boundaries? (e.g. in the above two implementations, both low-level exceptions may be caught and rethrown as "CacheInitialisationError" with some perhaps some additional diagnostic about the underlying cause). If they have such API-specific error types, do you go to the trouble of modelling an exception type hierarchy? So all your 'cache layer' exceptions inherit from 'CacheError' so a higher level can catch all "CacheExceptions" in one place? This seems like a lot of additional modelling effort, is it commonplace?
To my mind, the possible exceptions a API call may make (or equivalently, the errors it could return) are part of the API, and the errors need to be at the same conceptual level as the rest of the API. (So you can't have filesystem errors in a generic cache API).
What happens in practice?
- MikeCodeAwesome 14y agoGiven your scenario, you are indeed correct: throwing implementation-specific exceptions leaks internal details and further couples clients of the API. Can you imagine using such an API which forces you to continually handle new exceptions that arise from new implementations? As you suggest, it's better to create an exception for the API which then wraps the underlying exception. I would not go so far as to create a family or hierarchy of API exceptions unless there's a need to communicate each of them individually. What usually happens in practice is a combination of none and all the above. A bit of a mess, really.
- mathgladiator 14y agoWhen I "simplify" a bunch of stuff into a nice pretty "thing", I'll catch all exceptions (Throwable) and then re-throw if. Depending on the thing, I'll stick an enum in my exception. Usually, I only care about "should I retry?" or "give up" Personally, I think exceptions were a mistake for errors. I prefer how you do error codes via ocaml. let val = func ... in match val with Result(v) -> do nice thing | Error(500) -> retry | Error(503) -> retry | Error(_) -> abort