如何在Java中返回标志和可选消息?

时间:2010-08-23 09:03:17

标签: java readability

我想在Java中编写一个方法,用于验证某些条件是否存在某些数据,并确认数据有效会产生相应的错误消息。

问题是我们不能从一个方法返回多个东西,所以我想知道最佳解决方案是什么(在可读性和可维护性方面)。

第一个解决方案。很容易,但我们无法知道究竟是什么使检查失败:

boolean verifyLimits1(Set<Integer> values, int maxValue) {
    for (Integer value : values) {
        if (value > maxValue) {
            return false; // Out of limits
        }
    }
    return true; // All values are OK
}

第二种解决方案。我们有消息,但我们正在以一种我们不应该使用的方式使用异常(此外,它应该是特定于域的已检查异常,过多的开销IMO):

void verifyLimits2(Set<Integer> values, int maxValue) {
    for (Integer value : values) {
        if (value > maxValue) {
            throw new IllegalArgumentException("The value " + value + " exceeds the maximum value");
        }
    }
}

第三种解决方案。我们有一个详细的消息,但合同不干净:我们让客户端检查String是否为空(他需要读取javadoc)。

String verifyLimits3(Set<Integer> values, int maxValue) {
    StringBuilder builder = new StringBuilder();
    for (Integer value : values) {
        if (value > maxValue) {
            builder.append("The value " + value + " exceeds the maximum value/n");
        }
    }
    return builder.toString();
}

您会推荐哪种解决方案?或者有一个更好的(希望!)?

(注意:我编写了这个小例子,我的实际用例涉及异构数据的复杂条件,所以不要关注这个具体的例子并提出Collections.max(values) > maxValue ? "Out of range." : "All fine.": - )。)

11 个答案:

答案 0 :(得分:7)

如果您需要多个值,则应返回一个简单的类实例。以下是我们在某些情况下使用的示例:

public class Validation {
    private String          text    = null;
    private ValidationType  type    = ValidationType.OK;

    public Validation(String text, ValidationType type) {
        super();
        this.text = text;
        this.type = type;
    }
    public String getText() {
        return text;
    }
    public ValidationType getType() {
        return type;
    }
}

这对类型使用简单的枚举:

public enum ValidationType {
    OK, HINT, ERROR;
}

验证器方法可能如下所示:

public Validation validateSomething() {
    if (condition) {
        return new Validation("msg.key", ValidationType.ERROR);
    }
    return new Validation(null, ValidationType.OK);
}

就是这样。

答案 1 :(得分:5)

解决方案很简单:创建自定义VerificationResult类。它可以包含boolean status标记和String message字段,以及您可能想要添加的其他内容。不是返回Stringboolean,而是返回VerificationResult

此外,根据上下文,抛出异常实际上可能最终成为正确的事情。不过,必须根据具体情况逐案考虑。


替代解决方案:最后一次错误查询

您可以使用的其他选项是让验证返回boolean,并使用单独的方法,例如String whatWentWrongLastTime()用户可以在返回false的情况下进行查询。您必须非常小心可能会覆盖“最后”验证错误的任何并发问题等。

这是采用例如java.util.Scanner NOT 抛出任何IOException(构造函数除外)。要查询某些内容是否“出错”,您可以查询其ioException()方法,该方法返回上一个IOException,如果没有,则查询null

答案 2 :(得分:4)

IllegalArgumentException是真正意义上的方法:你向方法的调用者(契约)提出一些要求,但忽略了它们。在这种情况下,IAE是合适的。

如果这不能反映您的使用案例,我会使用其他解决方案之一。

答案 3 :(得分:3)

另一种方法 - 使用Status对象:

 public class Status {
   public final static Status OK = new Status("OK");
   private String message;
   public Status(String message) { this.message = message; }
   public String getMessage() { return message; }
 }

要验证,如果输入有效,则返回Status.OK或创建新的状态消息。

 public Status validate(Integer input, int maxValue){
   if (input > maxValue) {
     return new Status(
         String.format("%s value out of limits (maxValue=%s)", input, maxValue);
   }

   return Status.OK;
 }

使用验证程序很简单:

 Status status = validate(i, 512);
 if (status != Status.OK) {
   // handle the error
 }

答案 4 :(得分:1)

在这种情况下,返回'false'的方法看起来像业务逻辑结果而不是真正的异常。所以verifyLimits应该返回一个结果,而不是在'false'时抛出一个Exception。

 class VerifyLimitsResult{
       //Ignore get, set methods
        Integer maxValue;
        Integer value;

        public VerifyLimitsResult(Integer maxValue, Integer value) {
           this.maxValue = maxValue;
           this.value = value;
        }

        public boolean isOK(){
           return value==null;
        }

        public String getValidationInfo(){
           if(isOK()){
              return "Fine";
           }else{
              return "The value " + value + " exceeds the maximum value/n"
           }
        }
 }
....
VerifyLimitsResult verifyLimits4(Set<Integer> values, int maxValue) {

         for (Integer value : values) {
             if (value > maxValue) {
                   return new VerifyLimitsResult(maxValue, value);  
            }
        }
         return new VerifyLimitsResult(maxValue, null);  
}

答案 5 :(得分:1)

我认为最好的解决方案是创建自己的异常,保存尽可能多的错误描述信息。 应该是RuntimeException子类;您希望调用者必须处理验证失败,因为太多的程序员无法处理错误。通过使失败成为检查异常,你强制它们(你?)至少放入一些东西,如果它们对它愚蠢,代码审查可以相对容易地获取。我知道这是官僚主义的,但从长远来看,它可以提高代码质量。

完成后,请考虑是否需要在成功验证时返回值。只返回一个值,如果该值包含“哦,我现在就到这里”以外的信息(这在程序流程中是显而易见的)。如果确实需要返回结果,并且它需要是一个复杂的结果,那么一定要使用自定义类实例来保存它!不这样做只是拒绝使用语言为您提供的设施。

答案 6 :(得分:1)

如果你检查了合理数量的项目并担心你创建的对象数量以返回结果,那么{strong>替代会带有interface

首先,每当违反限制时,您都要创建一个interface

// A simple listener to be implemented by the calling method.
public interface OutOfLimitListener {
    // Called whenever a limit is violated.
    public void outOfLimit(int value, int maxValue);

    // ... Add additional results as parameters
    // ... Add additional states as methods
}

您可以添加参数和/或方法。例如,违规值的实际位置可以是参数。作为另一个示例,添加一个在每个测试结束时调用的方法,其中包含检查次数和违规次数的参数。

此接口的实现作为参数传递给您的检查方法。每当违反其中一个限制时,它就会调用监听器:

private boolean verifyLimits(Set<Integer> values, int maxValue, OutOfLimitListener listener) {
    boolean result = true; // Assume all values are OK
    for (Integer value : values) {
        if (value > maxValue) {
            listener.outOfLimit(value, maxValue);
            result = false; // At least one was out of limits
        }
    }
    return result;
}

最后,您只需实现界面即可使用此方法:

@Test
public final void test() throws IOException, InterruptedException {
    // Make up a test set of random numbers
    Set<Integer> testSet = new HashSet<Integer>();
    for(int i=0; i<10; i++) testSet.add((int) (Math.random() * 100));

    // Implement the interface once with appropriate reaction to an out-of-limit condition      
    OutOfLimitListener listener = new OutOfLimitListener() {
        @Override
        public void outOfLimit(int value, int maxValue) {
            System.out.printf("The value %d exceeds the maximum value %d\n", value, maxValue);
        }
    };

    // Call verification
    verifyLimits(testSet, 50, listener);
}

Android和其他GUI界面大量使用此模式。对我来说,当结果包含多个值时,它得到了首选方法。

答案 7 :(得分:0)

创建自己的自定义未经检查的异常,该异常从RuntimeException扩展而来。

答案 8 :(得分:0)

您可以使用简单的键值,使用HashMap,当然还可以使用预定义的键。 返回HashMap以进行进一步处理。

答案 9 :(得分:0)

我会投票支持第二个解决方案(使用IllegalArgumentException或定义特定的解决方案)。

一般来说,良好的做法是确保方法中的任何返回值都可以安全地被忽略(因为有一天有人会忘记检查它),并且在忽略返回值不安全的情况下,抛出/捕获总是更好例外。

答案 10 :(得分:0)

您可以将该标志作为布尔值返回并记录未经验证的测试结果,您无论如何都要记录它们......

假设你要检查数百万的价值。