Are exception hierarchies really useful?
It dawned on me that I've never seen a single exception hierarchy for which creating subclasses but catching the parent class was actually useful (except, of course, for the base Exception class, that has to be derived).
Are excepti开发者_如何学Con hierarchies really useful, xor should all exceptions be derived from language's base exception class?
Exception hierarchies are useful for grouping related exceptions together, when you need different granularity of catching in different places.
Putting all application exceptions in one place is the commonest use case. This allows you to catch MyAppException
any time that you want to trap all errors coming from your application, but still catch more specific exceptions when appropriate. (In .NET, the ApplicationException
class was meant for this, but it's been deprecated for various reasons.)
But you can also group exceptions together at module boundaries, or in any other way that makes sense. Use FooModuleException
for exceptions coming from the Foo
module, but catch and handle FooModuleMustFrobnicate
specially internal to Foo
. Or any equivalent situation.
I've used all of these patterns at different times.
TL;DR
- Exception hierarchies are useless for catching exceptions (except for base differentiation btw. non-recoverable system errors (Java
Error
) and "normal" runtime exceptions. The notion that "you need different granularity of catching in different places" is IMHO fundamentally flawed. (Except, to reiterate, to differentiate between exceptions and assertions and other system wide unrecoverable stuff.) - Small exception hierarchies make sense in todays languages as a means of constructing different errors with as rich information as possible.
I am slowly coming to consider the whole concept of arbitrarily typed exceptions, as I see them implemented and supposed-to-be-used in C++, C# and Java as totally useless.
The problem is not so much the exception hierarchy per se, but that when I write code, and exception are exceptional, I don't care what type of exception was thrown for the purpose of catching it.
In all my coding in C++, Java and C#, I found -- for the exceptional exceptions, that were not misused for control flow -- not one case where I wanted to filter (i.e. catch or not catch) the exceptions by their "hierarchical" type. That is, there are rare cases where a function throws 3 different exceptions, and I want to specifically handle one of them, and handle the two others as general "fail" (like described below) but I never ever encountered this in a case where the exception hierarchy was useful in that I could meaningfully group the two cases by base classes.
If any operation (where I care) in my program fails with any exception, then this operations is considered failed, and:
- I get what info I can from the caught object
- continue with contextualized error handling (re-throw, display error, log, re-try,
recover, etc.)
I think Java is the only of the three languages that at least has the right approach with their hierarchy, namely:
Throwable
not used in user codeError
the stuff you never want to catch - just core dumpException
- for all the stuff you basically always want to catch (except for when an Interface is botched and uses an Exception when it really shouldn't)
//
The hierarchy of C++'s <stdexcept>
has the right basic approach in differentiating between logic_error
- basically assertions - and runtime_error
- stuff that just unexpectedly failed. (The subclasses of theses are largely irrelevant when catching.)
Except of course that too much stuff derives directly from std::exception
so you can't make use of the differentiation provided by runtime and logic error. (And of course any code is free to throw whatever they want, so chances are good that there's a bunch of logic_error derived classes that really should be runtime_errors and vice-versa.
To quote another answer:
Exception hierarchies are useful for grouping related exceptions together, when you need different granularity of catching in different places.
but in my experience I never want "catching in different places", because it just doesn't make sense.
I either want catching, in which case I catch everything (modulo stuff like the Error
class exceptions from Java) or I don't want to catch, in which case the hierarchy is not needed, because there is no catch.
Take opening and reading from a file:
If there is (would be) any exception type I specifically care about, because I can do something about it ("recover"), then this should not be reported as an exception at all, because it is normal control flow. (If the error is such-and-such, I can do such-and-such basically locally.)
If on the other hand, I can't do anything about an error but record the file operation as failed, then having any catch granularity is utterly useless.
I've never created a single exception hierarchy for whole application, because an exception in general can bring different semantic and, therefore, needs to handled differently.
Some exceptions inform about system faults, another exceptions inform about bugs, and some other - about some exceptional condition, that could be gracefully recovered on higher level.
For this "business-logic" exceptions exception hierarchy can help you not to violate open-closed principle during overriding some methods in descendants.
Consider following example:
public abstract class Base
{
/// Perform some business-logic operation
/// and throw BaseFooException in certain circumstances
public abstract void Foo();
}
public class Derived1 : Base
{
/// Perform some business-logic operation
/// and throw DerivedFooException in certain circumstances.
/// But caller could catch only BaseFooException
public abstract void Foo() {}
}
class BaseFooException : Exception {}
class DerivedFooException : BaseFooException {}
Exception hierarchy may be helpful in custom framework or in specific libraries, but in general case (in business applications) I don't think that creating one deep and wide exception hierarchy is a good idea.
精彩评论