2

I'm writing a reusable library, and I'm looking for a way to handle the exceptions that might occur during the processing. For example, I have the following class:

public interface IObjectFetcher
{
    public IObject GetObject(int objectId);
}

public class WidgetFetcher : IObjectFetcher
{
    public IObject GetObject(int objectId)
    {
        var fileName = DetermineWidgetFileName(objectId);
        var widget = GetWidgetFromFile(fileName);
        widget.Frob = GetWidgetFrob(objectId);
        return widget;
    }

    private string DetermineWidgetFileName(int widgetId)
    {
        return someExternalDbService.GetFileNameForWidget(widgetId); //throws DbException
    }

    private Widget GetWidgetFromFile(string fileName)
    {
        return someExternalFileService.GetWidgetFromFile(fileName); //throws FileException
    }

    private Frob GetFrobForWidget(int widgetId)
    {
        return someExternalDbService.GetFrobForWidget(widgetId); //throws DbException
    }
}

How to handle the DbException and FileException so that:

  • the exception contract of IObjectFetcher.GetObject doesn't get too complex and can allow different implementations
  • there's a way to access the underlying cause of the exception, like a failed database connection or a missing file
  • there's a way to differentiate between the DbException caused by looking up a file name and the DbException caused by retrieving a Frob
  • it's possible to attach the exception metadata like the object ID and the file name in a structured way, eg. to extract exception message templates into a resource file

It seems to me that I should create custom exceptions for each step of the process (with the InnerException being either the DbException or the FileException), and then I have two ways of approaching the problem - either have those custom exceptions inherit from a single base class, or have them wrapped by a single exception class. Which approach is correct? Or should I simplify it further?

1 Answer 1

3

IMHO a complex exception class structure is often just over-engineering.

First make sure that the bullets you listed are really requirements of some user of your library.

I'd especially question the importance of differentiating between different exception causes. Who needs that? from my experience, an exception is meant to:

  • Tell the method's caller that it failed to fulfill its contract. This doesn't need multiple exception types.
  • Support the logging of problems, by providing a useful message and a stack trace. This also doesn't need different exception classes.
  • Allow the caller to retry the action that failed, maybe deciding on the best alternative based on the type of failure. Doing that can make good use of the exception type, but I've rarely seen such a pattern in real software.

So if you think some user of your library will want do do "intelligent retries" (and your library provides a chance to do it), go for an exceptions class hierarchy that allows for that, otherwise only make sure that the exceptions carry decent messages for logging purposes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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