Как вернуть флаг плюс необязательное сообщение на Java? - PullRequest
3 голосов
/ 23 августа 2010

Я хочу написать метод на 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");
        }
    }
}

Третье решение. У нас есть подробное сообщение, но контракт не является чистым: мы заставляем клиента проверять, пуста ли строка (для чего ему нужно прочитать 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 ]

7 голосов
/ 23 августа 2010

Если вам нужно более одного значения, вы должны вместо этого вернуть простой экземпляр класса. Вот пример того, что мы используем в некоторых случаях:

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);
}

Вот и все.

5 голосов
/ 23 августа 2010

Решение простое: создайте пользовательский класс VerificationResult. Он может иметь флаг boolean status и поле String message, помимо прочего, вы можете добавить. Вместо возврата String или boolean верните VerificationResult.

Кроме того, в зависимости от контекста, исключение может в действительности оказаться правильным. Это необходимо учитывать в каждом конкретном случае на основе конкретных сценариев.


Альтернативное решение: последний запрос об ошибке

Другой вариант, который вы можете использовать, - получить подтверждение boolean и использовать отдельный метод, например, String whatWentWrongLastTime(), который пользователь может запросить в случае возврата false. Вы должны быть очень осторожны с любыми проблемами параллелизма и т. Д., Которые могут перезаписать «последнюю» ошибку проверки.

Это подход, принятый, например, java.util.Scanner, который НЕ бросает любой IOException (кроме конструкторов). Чтобы запросить, «что-то пошло не так», вы можете запросить его метод ioException(), который возвращает последний IOException или null, если его не было.

4 голосов
/ 23 августа 2010

IllegalArgumentException - это путь, если он действительно означает, что: Вы предъявляете некоторые требования к вызывающему методу (контракту), но они игнорируются. В этом случае уместно IAE.

Если это не отражает ваш вариант использования, я бы использовал одно из решений других.

3 голосов
/ 23 августа 2010

Другой подход - использовать объект 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
 }
1 голос
/ 08 августа 2013

Если вы проверите разумное количество элементов и будете обеспокоены количеством создаваемых вами объектов для возврата результата, существует альтернатива с 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 используют этокартина сильно.Для меня это предпочтительный метод, когда результат содержит более одного значения.

1 голос
/ 23 августа 2010

Я думаю, что лучшее решение - создать собственное исключение, содержащее столько информации об ошибках, сколько вы хотите. Он должен не быть подклассом RuntimeException; Вы хотите, чтобы вызывающие абоненты имели дело с ошибкой проверки, потому что слишком много программистов не помещают обработку ошибок. Делая сбой проверенным исключением, вы заставляете их (вас?) Добавлять хотя бы что-то, и проверка кода может сравнительно легко начаться, если они глупы по этому поводу. Я знаю, что это бюрократический метод, но в конечном итоге он улучшает качество кода.

Как только вы это сделаете, подумайте, нужно ли возвращать значение при успешной проверке или нет. Возвращайте значение только в том случае, если это значение содержит информацию, отличную от «о, я сейчас здесь» (что очевидно из потока программы). Если вам нужно вернуть результат, и он должен быть сложным, непременно используйте пользовательский экземпляр класса для его хранения! Отказ от этого - просто отказ от использования тех возможностей, которые дает вам язык.

1 голос
/ 23 августа 2010

В этом случае метод, возвращающий значение «false», выглядит скорее как результат бизнес-логики, чем как настоящее исключение. Так что verifyLimits должен в любом случае возвращать результат, а не выдавать исключение при значении false.

 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);  
}
0 голосов
/ 08 августа 2013

Вы можете вернуть флаг как логическое значение и зарегистрировать результаты тестов, которые не подтверждают, вы все равно захотите их зарегистрировать ...

при условии, что вы будете проверять миллионы значений.

0 голосов
/ 23 августа 2010

Я бы проголосовал за второе решение (используя IllegalArgumentException или определив конкретное).

Как правило, хорошая практика - гарантировать, что любое возвращаемое значение из метода можно безопасно игнорировать (потому что когда-нибудь кто-нибудь забудет его проверить), а в случаях, когда игнорирование возвращаемого значения небезопасно, всегда лучше бросить / поймать исключение.

0 голосов
/ 23 августа 2010

Вы можете использовать простое значение ключа, используя HashMap, конечно, с предопределенными ключами Верните HashMap для дальнейшей обработки.

...