Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring exception handling

In one of my classes I have a call to a repository which has some error handling on it. I would like to refactor the error handling code because it is quite repetitive and the only thing that really changes is the message.

My code currently looks something like this:

public IList<User> GetUser()
{
    try
    {
        return _repository.GetUsers();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
    }
    ... etc
}

I could replace those lines in my catch block with a call to another method which takes an error message value and a logger message value. However I suppose I could also do this using an Action<> parameter but I am very inexperienced at using Func<> and Action<> and don't really see what benefit I would have using one of those over a method.

My question is really what is the best way to refactor this code and why does one way benefit over the other (as per my example above).

Thanks for any help.

like image 934
Serberuss Avatar asked May 20 '13 11:05

Serberuss


People also ask

What is refactoring and why is it not recommended?

Refactoring does “not” mean: Refactoring in the absence of safeguards against introducing defects (i.e. violating the “behaviour preserving” condition) is risky. Safeguards include aids to regression testing including automated unit tests or automated acceptance tests, and aids to formal reasoning such as type systems.

How do I use the @exceptionhandler annotation in Java?

The @ExceptionHandler annotation gives us a lot of flexibility in terms of handling exceptions. For starters, to use it, we simply need to create a method either in the controller itself or in a @ControllerAdvice class and annotate it with @ExceptionHandler:

What is meant by refactoring in programming?

Definition Refactoring consists of improving the internal structure of an existing program’s source code, while preserving its external behavior. The noun “refactoring” refers to one particular behavior-preserving transformation, such as “Extract Method” or “Introduce Parameter.”

How do you handle an exception in a method?

The exception handler method takes in an exception or a list of exceptions as an argument that we want to handle in the defined method. We annotate the method with @ExceptionHandler and @ResponseStatus to define the exception we want to handle and the status code we want to return.


2 Answers

You can use lambdas to help with this.

If you define your general-purpose error handler to accept a parameter of type Action then you can call that action in the error handler.

You don't need to worry about return values because the lambda you write at the point of call can take care of that.

For example, your general handler could look like this:

public void AttemptAction(Action action)
{
    try
    {
        action();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
        // Rethrow?
    }
    catch (SoapException ex)
    {
        ErrorMessages.Add("...");
        _logger.ErrorException("...", ex);
        // Rethrow?
    }
}

And then you could use it like this:

public IList<User> GetUser()
{
    IList<User> result = null;

    AttemptAction(() => result = _repository.GetUsers());

    return result;
}
like image 178
Matthew Watson Avatar answered Oct 15 '22 11:10

Matthew Watson


Assuming the exception types are always the same but the messages are different, you can do this:

static public T Try<T>(string webMessage, string soapMessage, Func<T> func)
{
    try
    {
        return func();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add(webMessage);
        _logger.ErrorException(webMessage, ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add(soapMessage);
        _logger.ErrorException(soapMessage, ex);
    }
}

This Try-method will use a delegate of type Func<T> to call a function and return its value. The function will be inside the same try-catch block. The messages are provides via parameters. Now, somewhere else in your code, you could call this like:

var users = Try("My web message.", "My soap message.", () => _repository.GetUsers());

Or, in your case even shorter (when not using parameters):

var users = Try("My web message.", "My soap message.", _repository.GetUsers);

Of course you can modify and arrange the parameters of Try to your own liking.

In case you are mixing method with and without return types, it is better not to use the Func but the Action. This will be able to comply to all situations:

static public void Try(string webMessage, string soapMessage, Action action)
{
    try
    {
        action();
    }
    catch (WebException ex)
    {
        ErrorMessages.Add(webMessage);
        _logger.ErrorException(webMessage, ex);
    }
    catch (SoapException ex)
    {
       ErrorMessages.Add(soapMessage);
        _logger.ErrorException(soapMessage, ex);
    }
}

But this solution makes the code a tiny bit more difficult to read / maintain:

IList<User> users;
Try("My web message.", "My soap message.", () => users = _repository.GetUsers());
like image 29
Martin Mulder Avatar answered Oct 15 '22 11:10

Martin Mulder