A Pattern in the Making.
The core idea is this: You've got a nicely layered system, as per the OSI model or a FourLayerArchitecture
. Now, each layer has its own notion of what's what. Each layer does something different and each layer, in effect, is a server (for lack of a better word). Therefore, each layer should ConvertExceptions
This applies, I think, in more generality. I'd say a context is something like a coherent set of tasks performed by a portion of the code, along with the assumptions made in performing those tasks.
That is, it's almost like a module. Except that, to a large extent, contexts are run-time dependent notions (especially when you use the StrategyPattern
to separate out crucial assumptions).
Any time you use the TinyClientsTinyServers
metaphor, you have multiple contexts and therefore, should be throwing and converting exceptions.
See also: HomogenizeExceptions
Yabbut, if you HomogenizeExceptions
per layer/context/module, how do you NameTheProblemNotTheThrower
? -- RandyStafford
In theory, every responsibility is handled by only one class. Therefore, in theory, each individual kind of problem can only occur in one class. If you make your exception hierarchy match your class/package hierarchy and you make your class/package hierarchy match your layers/contexts then you can create ExceptionPerContext
supertypes and use sub-types to NameTheProblemNotTheThrower
- except you name the thrower as a side-effect of naming the problem and you use the name of the thrower in your "throws" clauses as a dependency management mechanism.
I say that all of this is "in theory" because it seems like a dubious proposition that it will work in practice. In my experience it's been a mixed bag. I like to TranslateExceptions
at package boundaries. I usually don't care about type information when I do this so I just create a new exception object that tacks some contextual information onto a string that's stored in the old exception and throw the new exception up the chain until my UI can handle it. If I need more complex handling, however, I use NestedException
s and unpack them by hand in the handler - I really try to avoid needing more complex handling. I suspect that if I really needed a lot of complex handling I'd come up with an entirely different solution, but so far bubbling the exception up to the UI has almost always worked for me. -- PhilGoodwin
I like and agree with everything you've written on Wiki about exception handling, Phil, especially your pattern on LetExceptionsPropagate
. The thing that bothers me about the de-facto, package-based approach to HomogenizeExceptions
is that all exception-throwing methods of classes in a given package declare only that they throw some general type of exception for that package. While I agree with the benefits of doing this (it reduces the number
of exception types in throws clauses, and it insulates clients of methods in that package from changes in the types of exceptions thrown by code called by those methods), it seems there are a couple of problems with it as well. First, the programmer has to resort to other information (e.g., javadocs, hopefully) to find out which specific RefinedExceptions
are thrown from those methods. Second, it seems inconsistent with DontThrowGenericExceptions
- if method bodies should not instantiate and throw generic exceptions, then it seems that for clarity and consistency, neither should throws clauses declare that they throw only general exceptions (which begs the question, I guess, of how general is too general, especially for a throws clause). Third, it requires the creation and maintenance of additional exception classes for each new package introduced. I've been using a different approach to HomogenizeExceptions
, described on that page, that has worked well for me so far on two different application development projects. Best Regards, -- RandyStafford