3

On a side project I'm working on I came up with a way of handling exceptions that's adjustable by configuration. So a try/catch block might look like this:

try
{
    fileHelper.MoveFile(file, destFolder);
}
catch (IOException ex)
{
    if (exceptionHandler.HandleException(ex))
    {
        return ResultCode.Fail;
    }
    throw;
}

The granularity of exception handling is controlled through an enum that's set in the app.config file:

public enum SystemFailureMode
{
    AlwaysThrow = -1,
    Aggressive = 0,
    Standard = 1,
    Lenient = 2
}

This determines whether an exception is "handled" and returned as an error, or if it bubbles up and crashes the system. At first I thought I'd uncovered a new design pattern but now it seems like it's probably an anti-pattern. What do you guys think?


After reviewing the comments and thinking about it myself, I've decided this is definitely an anti-pattern. The fact that this has the potential to turn writing unit tests into a nightmare is enough to turn me off of it.

  • 5
    I vote "anti pattern". This has the potential to massively complicate debugging user issues. Me: "What do you mean you changed your error handling to 'Lenient'?" Them: "But it made the application more stable!" Me: "It only made it appear more stable." Them: "WTF!?" – MetaFight Feb 18 '14 at 1:22
  • Hello all. I've removed some of the Meta comments here, as they became obsolete after the question was re-opened. There's an open Meta question on this question, further discussion on the question's topicality should go there. Since the question has already gone through a close - re-open cycle, if you think it should be closed again, please go through the Meta discussion and raise your concerns there before casting your close vote. – yannis Feb 22 '14 at 14:21
9

In my opinion, the nicest way for Exception handling in C# is the same exposed in this article: http://johannesbrodwall.com/2014/02/07/c-tricks-slimming-down-your-controllers/ (look for "Exception handling")

Please note that the same pattern can be used for Java: http://johannesbrodwall.com/2013/09/25/offensive-programming/

The most important concept is:

"(almost)Never handle exceptions locally"

That's the simplest and the cleanest way to handle exceptions in both Java and C#. In your case you catch the exception everytime it can happen and then you configure how to handle them, am I right? Now isn't that a hint that you should centralize the exception handling elsewere? Instead of repeating the same pattern try - catch -handleExceptionInAConfigurableWay - throw every time. I personally don't see any point in configuring a global FailureMode to handle exceptions, if you have an ApplicationFilter like the one in the 1st article, and you want to change how Exception are handled, just change the behaviour of the OnException template method.

Result:

+1: The actual handling of exceptions should be centralized

-10: Not in this way!

Of course it's not possibile to throw always an Exception and handle it in a single "application filter" point. Sometimes you have some operations to do in case of exceptions (e.g. rollback a transaction), or in every case (e.g. an open file to close in finally block), but in any other situations, if you have to just log the exception stacktrace or pop an error message in some GUI, then just throw the exception and handle it in a single global point.

Using Exceptions as a part of your business logic has never been a good pattern, and the epic failure of Java checked exception is a proof of that.

  • 2
    "Using Exceptions as a part of your business logic has never been a good pattern, and the epic failure of Java checked exception is a proof of that." - There it is. – Repo Man Feb 20 '14 at 22:54
2

It depends on the definition of "exception"

What does it mean to have an exception? The usual thinking is that something prevented execution from going normal. Something is "wrong." If it is not handled, nothing in the future will be "right."

The big question for you is, "what is the state of the system when the exception is thrown?" What you are talking about is adjustable logic in the recovery after an exception is thrown. Whether that makes sense or not is TREMENDOUSLY case specific. It needs to make logical sense to say "let the user decide whether I should stop here, or if I should do my backup plan." If it makes sense to leave that choice in the hands of the user, by goodness, do it! If that feels funny, you probably should not give them such power. In some cases, exception cleanup is a VERY messy business, and letting the user in on that dirty little escapade can lead in very bad directions.

That being said, I have seen the extreme version of your pattern: there was one exception handler for the whole program. If an exception was thrown, and PRODUCTION_MODE was chosen, the entire program virtually restarted.

0

You have implemented a "resumable exception". Many people consider resumable exceptions to be an anti-pattern, partly because it is almost never necessary, partly because it is a pattern that can be abused.

Having implemented a "resumable exception", you then add a signal to control how the exception is handled. This is normal, and is not the anti-pattern. Common status values include "force ignore exception type", "handle exceptions as programmed", and "throw directly to top level".

In my opinion, resumable exceptions are a Good Thing when dealing with un-attended, parallel, synchronous/asynchronous processes with shared resources. You don't want an exception on your process control to kill the whole server process. You don't want an exception to bubble up to the 'top', because there is nothing and nobody at the 'top'. Most exceptions you don't want to treat as exceptions at all.

The best known example of resumable exceptions is BASICs "on error resume next", equivilant to your "return ResultCode.Fail". "Error" is an archaic term for "Exception": "resume next" is the exception-handler control signal. This pattern was commonly used to wrap serial communications, where "file errors" are not "exceptional"

Your "return ResultCode.Fail" handles minimal cases like that, where you can isolate the part of the program where you want to turn off exception handling.

On the other hand, when you find yourself wrapping every single line into a seperate try-catch block, it's time to find a different language, not time to build it yourself.

0

If you do this, you will have created a massively complex and effectively untestable system; the number of possible system behaviors goes up exponentiallty with the number of this type of configuration setting. As a result, it commonly won't work.

But on the upside, the failures introduced will, in principle, always be fixable by another configuration file change. And in some cases the correct configuration settings to change may be discoverable within only a few days of investigation...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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