我想在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."
: - )。)
答案 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
字段,以及您可能想要添加的其他内容。不是返回String
或boolean
,而是返回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)
您可以将该标志作为布尔值返回并记录未经验证的测试结果,您无论如何都要记录它们......
假设你要检查数百万的价值。