Exceptions and Abstractions

不打扰是莪最后的温柔 提交于 2019-12-04 12:26:05

I would expect exceptions to talk in terms of what I've asked the originating method to do. e.g.

read -> ReadException
connect -> ConnectException
buildPortfolio -> FailedToBuildPortfolioException

etc. This abstracts away what's going on under the covers (i.e. are you connecting via sockets etc.). As a general rule, when I create an interface for a component, I often create a corresponding exception or set of exceptions. My interface will be called Component, and my exceptions are usually ComponentException (e.g. RateSource and RateSourceException). It's consistent and easy to export to different projects as a complete component set.

The downside is that you create quite a lot of exceptions, and you may have to perform quite a lot of translations. The upside is that (as you've identified) you get little to no abstraction leakage.

At some point during the hierarchy of method calls (and thus exceptions) you may decide that no recovery can take place (or it's at an inappropriate place) and translate to unchecked exceptions to be handled later.

I know this is tagged as "language-agnostic", but I don't think it really is. Coming from a C++ perspective, I expect very few basic operations to throw an exception - the C++ Standard Library only uses exceptions in a very few places. So my own code is often the first place where exceptions can be generated. In that code, I like a very flat hierarchy - I don't want to be messing with hundreds of catch() clauses later in the code, and have never understood Java and C#'s apparent obsession with creating Baroque heirarchies of class and namespace.

So, for my C++ code - one type of exception, containing a meaningful error message, per library. And one for the final executable.

I think there are two questions hidden here: a) When should one hide an exception behind a different exception. b) When should one use a custom exception for this.

a) I'd say: when ever an exception travels across the border of two layers in the application, it should get hidden behind an exception that is more apropriate for the new layer. Example: because you are doing some remote stuff, you get a ConnectionWhatEverException.

But the caller shouldn't be aware of Connection problems. Since he just wants to get some service performed, so he gets a ServiceOutOfOrderException. The reason for this is: Inside the layer, doing remoting, you might to do something usefull with a ConnectionException (retry, write into a backout queue ..). Once you left that layer, nobody knows how to handle a ConnectionException. But they should be able to decide, what do do, when the Service does not work.

b) When there is no matching existing Exception. There are a couple of useful Exception in Java for example. I use IllegalState and IllegalArgument quite often. A strong argument for a new exception class is, if you have some useful context to provide. For example the name of the service that failed could be an argument of a ServiceFailedException. Just don't create a class for every method call, or anything to that effect. 100 Exception classes aren't a problem, as long as they have different behavior (i.e. at least different fields). If they differ only by name and reside on the same abstraction level, make them one Exception, and put the different names in the message or a single field of that exception class.

c) At least in java there is the discussion about checked exceptions. I wrap those directly in an unchecked one, because I hate the checked kind. But that is more an opinion then advice.

Is there any case where you would get NoConnectionException which isn't caused by an IO issue? Conversely, is knowing whether the cause is IO based or not going to help the client recover sensibly?

When should you throw a custom exception?

I. When you can provide more (diagnostic) information.

Note: this additional information may not be available at the place where the original exception (IOException) was thrown. Progressive layers of abstractions may have more information to add like what were you trying to do which led to this exception?

II. When you must not expose implementation details: i.e. you want the (illusion of?) abstraction to continue.

This may be important when the underlying implementation mechanism can change. Wrapping the underlying exception in a custom exception is a good way of insulating your clients from implementation details (by lifting the level of abstraction)

III. Both I and II

NOTE: Furthermore your clients should be able to tune into the exact level of information they are interested in or rather they should be able to tune out anything they are not interested in. So it's a good idea to derive your custom exceptions from IOException.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!