简体   繁体   中英

How to approach base exception thrown by API method?

Say I'm dealing with forced to use a library API method that throws some sort of nondescript base exception; for example "throws Exception" in Java. Assume I do not have the option to modify the library source and I must deal with the base exception any time I call the API method from my own methods . For some context, my code might look like this without intervention:

public void myMethod() throws Exception { // I don't want to do this.
    someAPIObject.theirMethod(); // API method throwing base exception.
}

And here might be the API method I'm calling in to:

public void theirMethod() throws Exception { // This is my problem.
    // Does stuff that could cause problems, but is
    // lazy and implicitly throws base exception.
    // Now it's my problem!
}

My question is: how do I best go about dealing with this base exception being thrown at my methods ? I assume it is in my best interest to somehow preserve all of the original exception information while propagating something more useful than a base exception. For example, I've considered catching and storing the base exception in my own exception type and throwing that instead:

public void myMethod() {
    try {
        someAPIObject.theirMethod(); // API method throwing base exception.
    } catch (Exception e) {
        throw new MySpecificException(e); // Re-throw my own exception.
    }
}

I'm not looking for opinions but rather some solid and straightforward evidence (pros) as to why a particular solution is a good solution, as well as any cavaets (cons). My focus is on Java, but I am curious of any general concepts or "best-practices".

This question will undoubtedly be closed as "Too Broad" or "Opinion Based" but I'll throw in my 2c before it does.

Your second code sample should (nearly) always be the way to go:

public void myMethod() {
    try {
         someAPIObject.theirMethod(); // API method throwing base exception.
    } catch (Exception e) {
        throw new MySpecificException(e); // Re-throw my own exception.
    }
}

My primary reason for this is that I do not want a leaky abstraction . For example, say I have some kind of repository for accessing users with the following interface:

public interface UserRepository {
    public User byId(UserId id);
}

And I have this implemented by a MySql database, so have the following concrete class:

public class MySqlUserRepository implements UserRepository {
    public User byId(UserId id);
}

Inside this class I will be needing to handle JDBC exceptions. If I just let them propagate through the interface, like this:

public interface UserRepository {
    public User byId(UserId id) throws SqlException;
}

Then a client of my code now knows it is using JDBC in the background. If I wrap this, like you have, then the underlying data store is completely encapsulated, which is one of the points of having an abstraction in the first place. If I provide an implementation that uses some other datastore, eg Redis, then SqlException has no meaning anymore but I would need to update the contract to now make the methods throw the Exceptions that the specific datastore might throw.

Assuming that you know a complete list of exception subclasses that can be thrown by the API, you may be better off "cracking" the exception type with a runtime check, and wrapping the method into your own wrapper that throws specific exceptions.

Here is an example: let's assume that the API can throw three specific subclasses of Exception - namely, ExceptionOne , ExceptionTwo , and ExceptionThree . You could build a wrapper like this:

class SomeApiWrapper {
    public void myMethod() throws ExceptionOne, ExceptionTwo, ExceptionThree {
        try {
            someAPIObject.theirMethod();
        } catch (Exception e) {
            crackException(e);
        }
    }
    private static void crackException(Exception e) throws ExceptionOne, ExceptionTwo, ExceptionThree {
        if (e instanceof ExceptionOne) throw (ExceptionOne)e;
        if (e instanceof ExceptionTwo) throw (ExceptionTwo)e;
        if (e instanceof ExceptionThree) throw (ExceptionThree)e;
        throw new RuntimeException("Caught an exception of unexpected type", e);
    }
}

The users of your API will not have to catch Exception (which is a very bad thing) and retain all of the information embedded in the original exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM