Search code examples
exceptiontheorycode-duplication

Throw extra exception to avoid code duplication


First of all, I know the standard answer will be that exceptions are never to be used for flow control. While I perfectly agree with this, I've been thinking a long time about something I sometimes did, which I'll describe with the following pseudo-code:

try
    string keyboardInput = read()
    int number = int.parse(keyboardInput)
    //the conversion succeeds
    if(number >= 1000) 
        //That's not what I asked for. The message to display to the user
        //is already in the catch-block below.
        throw new NumberFormatException() //well, there IS something wrong with the number...
 catch(NumberFormatException ex)  //the user entered text
    print("Please enter a valid number below 1000.")

First of all, take this example in a very abstract way. This does not necessarily have to happen. The situation simply is:

A user input needs to be constrained and can go wrong in 2 ways, either by a thrown exception the language defines, or by a check. Both errors are reported by the user in the same way, because they do not need to know the technical difference of what caused it.

I have thought of several ways to solve it. To begin with, it would be better to throw a custom made exception. The problem I then face is, if I catch it locally, what to do with the other exception? In se, the custom exception would be cause for a second catch-block, in which the message would be copied into just as well. My solution:

//number is wrong
throw new MyException()
catch(NumberFormatException ex) 
    throw new MyException()
catch(MyException ex) {
    print("Please enter...")

The meaning of the exceptions' names is everything here. This application of custom-made exceptions is widely accepted, but essentially I didn't do anything different from the first way: I forced to go into a catch-block, albeit by throwing a custom exception rather than a standard-library one.

The same way applied to throwing the exception on to the calling method (thus not having a catch block for the custom exception) seems to make more sense. My method can go wrong in what is technically two ways, but essentially one way: wrong user input. Therefore, one would write a UserInputException and make the method throw this. New problem: what if this is the main method of an application?

I'm not currently struggling with a specific application to implement this kind of behaviour, my question is purely theoretical and non-language specific.

What is the best way to approach this?


Solution

  • I think you have essentially a few ways to go about it with minimal code duplication in mind:

    1. Use a boolean variable/store the exception: If there was an error anywhere in the the general logic of the specific task you are performing, you exit on the first sign of error and handle that in a separate error handling branch.

      Advantages: only one place to handle the error; you can use any custom exception/error condition you like.

      Disadvantages: the logic of what you are trying to achieve might be hard to discover.

    2. Create a general function that you can use to inform the user about the error (pre-calculating/storing all information that describes the general error, e.g. the message to display the user), so you can just make one function call when an error condition happens.

      Advantages: the logic of your intent might be clearer for readers of the code; you can use anu custom exception/error conditon you like.

      Disadvantages: the error will have to be handled in separate places (although with the pre-computed/stored values, there is not much copy-paste, however complex the informing the user part).

    3. If the intent is clear, I don't think throwing exceptions from within your try block explicitly is a bad idea. If you do not want to throw one of the system provided exceptions, you can always create your own that derives from one of them, so you only need a minimal number (preferably one) of catch blocks.

      Advantages: only one place to handle error condition -- if there is essentially only one type of exception thrown in try-block.

      Disadvantages: if more than one type of exception is thrown, you need nested try-catch blocks (to propagate the exceptions to the most outward one) or a very general (e.g. Exception) catch block to avoid having to duplicate error reporting.