views:

90

answers:

5

I am implementing an interface which defines a method that can throw an exception if the parameters are not valid. What constitutes valid parameters depends on the implementing class. The interface also defines an isValid() method which can be used to check the parameters but returns a boolean rather than throwing an exception. I have found that implementing both methods will cause a lot of duplication. Consider this made up example:

public class Something implements SomeInterface {

    // Other class stuff

    public void doTheThing(SomeParameter sp) throws SpecificRuntimeException {
        if(sp == null) throw new ParameterCannotBeNullException();
        if(sp.getNonZeroInt() == 0) throw new ShouldBeNonZeroException();
        if(!sp.someOtherCondition()) throw new SomeConditionNotMetException();

        ...
    }

    public boolean isValid(SomeParameter sp) {
       if(sp == null) return false;
       if(sp.getNonZeroInt() == 0) return false;
       if(!sp.someOtherCondition()) return false;

       ...
       return true;
    }
}

The problem is the checks in both methods must be consistent, and are essentially duplicated logic. I've been trying to consolidate the checks so that both methods use the same checks but the behaviour is still retained. Some things I considered:

  1. in doTheThing() have the line if(!isValid(sp) throw new RuntimeException();
  2. separate the exception throwing part into a separate, private method, say checkParameter() and in isValid() do: try { checkParameter(sp); return true; } catch (SpecificRunTimeException e) { return false; }

The problem with 1. is that the specific exception is lost, and I want to provide as detailed an exception as possible. The problem with 2. is using the exception mechanism seems... wrong somehow. This part of the code may be performance sensitive, and I don't want to depend on something that's fundamentally slower than it needs to be (if I have to do it this way and profiling doesn't show a problem, fair enough... but what if is a problem?). Or has the performance hit of using exceptions this way been shown to be negligible?

What is the best way to refactor this code to use the same validity checking logic?

A: 

Your urge to "say it once and only once" is laudable. Good thinking; it'll be worth the effort.

Maybe you can create an abstract class that implements the interface and provides the default implementation for isValid().

If your object is mutable and has setters, perhaps you could move the check that throws IllegalArgumentException into each one and then call them. You'll have a more fine-grained view of what went wrong that way.

duffymo
My example has many checks for a single parameter, so splitting it into setters isn't going to work. I'm not sure I get what you mean with the abstract class - what would the implementation within a default `isValid()` look like?
Grundlefleck
+1  A: 

I don't know if it's the best way, but a way is to have an internal method like this:

private boolean isValidInternal(SomeParameter sp, boolean throwIfInvalid)
    throws SpecificRuntimeException

Then you call isValidInternal with true from the doTheThing method, and false from the public isValid method.

Edit: And then to implement isValidInternal, you have the logic for testing validity once, but either return false or throw an exception as per the throwIfInvalid flag.

Ash
+1 I could see this working. Thanks.
Grundlefleck
I would object to this in a code review as being overengineering. Much better for isValid() to just call the other method and catch. Sure, it feels as dirty as `instanceof`, but like `instanceof`, sometimes it's just what you've gotta do.
Kevin Bourrillion
+2  A: 

What if your create a isValidParam method that returns a bean like:

class ValidBean {
  private boolean isValid;
  private Exception exceptionOnInvalid;
  public ValidBean(boolean isValid, Exception exceptionOnInvalid) {...}
  // accessors
}

With the method being:

private ValidBean isValidParam(SomeParameter sp) {
       if (sp == null) {
          return new ValidBean(false, new ParameterCannotBeNullException());
       }
       if (sp.getNonZeroInt() == 0) { 
         return new ValidBean(false, new ShouldBeNonZeroException());
       }
       if (!sp.someOtherCondition()) {
          return new ValidBean(false, new SomeConditionNotMetException());
       }
       …
       return new ValidBean(true, null);
    }

And then you do:

public boolean isValid(SomeParameter sp) {
 return isValidParam(sp).isValid();
}

And

public void doTheThing(SomeParameter sp) throws SpecificRuntimeException {
  ValidBean validbean = isValidParam(sp); 
  if (! validBean.isValid()) {
      throw validBean.getExceptionOnInvalid();
  }
  ...
}
dpb
+1 I like this. The good thing would be that an abstract class could do most of the work, subclasses only need to provide `isValidParam()`.
Grundlefleck
I would object to this in a code review as being overengineering. Much better for isValid() to just call the other method and catch. Sure, it feels as dirty as instanceof, but like instanceof, sometimes it's just what you've gotta do.
Kevin Bourrillion
@Kevin: the only thing that has stopped me doing that so far is the chance that there *may* be a performance hit. Have you ever found any difference in performance to be anything but negligible?
Grundlefleck
No, I have not. Don't even worry about it.
Kevin Bourrillion
A: 

The fact that your code checks the arguments for sanity and returns runtime exceptions is a strong indication that you want to report programming error, i.e. your API is used/called in a way it should not be used/called. Since you could expect that your API is called with valid arguments, I wouldn't expect that your second solution to be problematic.

P.S.: You should not use runtime exception types in a method's throws declaration. But you should state those runtime exceptions in the method's Javadoc @throws documentation.

Frank Grimm
Why should I not use runtime exception in the signature? As far as I knew it made no difference, it was just another piece of documentation to go along with `@throws`. Do you have a source where I could read up on this?
Grundlefleck
The advice was taken from Bloch's famous book Effective Java.In Item 58 Bloch advises: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors. [...] Use runtime exceptions to indicate programming errors. The great majority of runtime exceptions indicate _precondition violations_. [I.e.] [...] [F]ailure by the client of an API to adhere to the [...] API specification.And in Item 62 he writes: Use the Javadoc @throws tag to document each unchecked exception, but do _not_ use the throws keyword to include unchecked exceptions in the method declaration.
Frank Grimm
Great, thanks. From reading it, it seems it's to make it clear to the reader which are checked and which are unchecked, which I hadn't considered. Cheers.
Grundlefleck
+1  A: 

This can be a murky issue; when it comes to exceptions in APIs I've never found a really clear, logical, satisfactory answer to every case.

Do you expect every user of isValid() to be using it to guard a subsequent call to doTheThing()? If so, the client code would be clearer to use the standard try/catch idiom instead, and you may not need an isValid() method at all.

(You'd still have to ponder whether the exception should be checked or unchecked; the line can be a fine one and I won't go into it here.)

And if you don't see isValid() being used this way, then just implement it with a try/catch of your own; I know it feels dirty, but it's better than the alternatives!

Kevin Bourrillion