1
\$\begingroup\$

I am developing an Android Library which will be distributed to other developers. The library itself is a wrapper of the industry specific one. This makes the API development a bit less flexible. The main goal is to provide an easy and understandable API to the external developers.

For now I've selected an asynchronous approach as a basis for the public API. The simplified version of the public API look like this:

MyApi{
 public void login(String user, String password, LoginListener listener){...}
 public void doAction1(String actionParam, Action1Listener listener){...}
 public void doAction2(String actionParam1, String actionParam2, Action2Listener listener){...} 
}

All the names are selected just for this question. So the listener (interface) in each method may have arbitrary number of methods. I try to limit the number as much as possible. One method that each of the listeners have is onError(XXXError).

So there is a separate type of Error for each type of listener. Errors extend RuntimeException class an may have common traits. Such as "caused by network problem" error.

For example, this is how the LoginListener interface looks:

public interface LoginListener {
    void onLoginSuccess();
    void onLoginFailed(LoginError error);
}

As a result the user of my API may either display some default error message or do an additional check. Additional check may be required since the error may be caused by different communication problems (network connection, bad input params, internals server error, etc.) in case.

Here is the implementation:

LoginListener loginListener = new LoginListener(){
        @Override
        public void onLoginSuccess() {
            //proceed
        }

        @Override
        public void onLoginFailed(LoginError error) {
            if(error.isNetworkError()){
                //ask user to check network connection
            }else{
                //display default message
            }
        }
    };

For Action1 and Action2, the errors are more complicated and may involve even instanceof comparison to get the exact reason.

Isn't error typing an overhead? What are good examples of error handling/notification?

An option of implementing one MyApiError class with an enum of causes, sounds like a non-flexible and misleading one.

\$\endgroup\$
1
\$\begingroup\$

First of all, I would recommend to use one class/interface for all callbacks.

So, the LoginListener, Action1Listener and Action2Listener should be refactored to single type:

public interface Handler {
}

The second concern is that you're trying to cover all possible errors in your own *Error classes.

From my point of view, you should keep original exceptions.

public interface Handler {
    void failure(Exception e);
}

To handle result of API call, you probably want to use generics:

public interface Handler<T> {
    void success(T result);
    void failure(Exception e);
}

So, at this point, should have only one interface as a handler of API call, and several beans that represent result.

The base of result:

public interface Res { 
}

The concrete result:

public class RegistrationCallResult implements Res {
    // fields.
}

As for showing toasts or notifications, you can extend Exception and create your own one:

public class InternalException extends Exception {
    public InternalException(Exception e, ErrorType t) { /* save parameters */ }
    public ErrorType type() { /* return type */ }
}

Then Handler will be:

public interface Handler<T> {
    void success(T result);
    void failure(InternalException e);
}

Then, to show toast:

public class NotificationManager {
    public void displayToast(ErrorType t) {
        switch (t) {
            case ...:
                Toast.show(...);
                break;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ If you suggest using only one interface, how would you differentiate between errors? Let's say error for Action1 may be caused by: poor network connection, bluetooth communication problem, incorrect data provided by user, internal implementation bug. I like the idea of using only one interface. Question is: how will user understand which of the exception may arrive in onError? Just with documentation? \$\endgroup\$ – Zzokk Oct 16 '14 at 15:17
  • \$\begingroup\$ @Zzokk, I've updated answer, with the one of possible solutions. \$\endgroup\$ – pbespechnyi Oct 16 '14 at 15:26
  • \$\begingroup\$ I would say that InternalException is actually any error that may occur. And ErrorType is the cause of the error. Right? In other words ErrorType will be {BLUETOOTH, NETWORK, BAD_INPUT_DATA, INTERNAL} \$\endgroup\$ – Zzokk Oct 16 '14 at 15:30
  • \$\begingroup\$ @Zzokk, correct. \$\endgroup\$ – pbespechnyi Oct 16 '14 at 15:34
  • \$\begingroup\$ I am bit concerned of creating a general ErrorType enum... It means that API "allows" BLUETOOTH type to be processed in the login callback. Which is impossible. Isn't it misleading? Am I to strict about it? \$\endgroup\$ – Zzokk Oct 16 '14 at 15:40

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.