Правильное решение в Java: проанализируйте 2 числа из 2 строк и затем верните их сумму - PullRequest
84 голосов
/ 25 августа 2011

Довольно глупый вопрос.Учитывая код:

public static int sum(String a, String b) /* throws? WHAT? */ {
  int x = Integer.parseInt(a); // throws NumberFormatException
  int y = Integer.parseInt(b); // throws NumberFormatException
  return x + y;
}

Не могли бы вы сказать, хорошо ли это Java или нет?То, о чем я говорю, это NumberFormatException - непроверенное исключение.У вас нет , чтобы указать его как часть sum() подписи.Более того, насколько я понимаю, идея непроверенных исключений состоит лишь в том, чтобы сигнализировать о том, что реализация программы неверна, и, более того, перехват непроверенных исключений является плохой идеей, поскольку это похоже на исправление плохой программы во время выполнения .

Кто-нибудь уточнит, пожалуйста:

  1. Я должен указать NumberFormatException как часть сигнатуры метода.
  2. Я должен определить свое собственное проверенное исключение (BadDataException), обработайте NumberFormatException внутри метода и перебросьте его как BadDataException.
  3. Я должен определить свое собственное проверенное исключение (BadDataException), проверить обе строки как-то как регулярные выражения и бросить мойBadDataException если не совпадает.
  4. Ваша идея?

Обновление :

Представьте, что это не открытый кодрамки, которые вы должны использовать по какой-то причине.Вы смотрите на подпись метода и думаете: «Хорошо, он никогда не бросает».Затем, однажды, вы получили исключение.Это нормально?

Обновление 2 :

В некоторых комментариях говорится, что мой sum(String, String) плохой дизайн.Я абсолютно согласен, но для тех, кто считает, что оригинальная проблема никогда бы не появилась, если бы у нас был хороший дизайн, вот дополнительный вопрос:

Определение проблемы таково: у вас есть источник данных, где хранятся числакак String с.Этот источник может быть файлом XML, веб-страницей, окном рабочего стола с 2 полями редактирования, чем угодно.

Ваша цель - реализовать логику, которая принимает эти 2 String с, преобразует их в int с и отображает окно с сообщением "сумма равна ххх".

Независимо от того, чтоподход, который вы используете для разработки / реализации этого, вы будете иметь эти 2 точки внутренней функциональности :

  1. Место, где вы конвертируете String в int
  2. Место, куда вы добавляете 2 int s

Основной вопрос моего оригинального сообщения:

Integer.parseInt() ожидает правильно строка для передачи.Всякий раз, когда вы передаете неверную строку , это означает, что ваша программа неверна (не " ваш пользователь идиот").Вам нужно реализовать фрагмент кода, где с одной стороны у вас есть Integer.parseInt () с MUST семантикой , а с другой стороны, вы должны быть в порядке со случаями, когда ввод неправильный - СЛЕДУЕТсемантика .

Итак, вкратце: как мне реализовать СЛЕДУЕТ семантике , если у меня только ДОЛЖНЫ библиотеки .

Ответы [ 18 ]

49 голосов
/ 25 августа 2011

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

 public static int sum(int a, int b);

С вашей подписью метода я бы не что-либо изменил бы. Либо вы

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

Следовательно, обработка исключений в этом случае становится проблемой документация .

35 голосов
/ 25 августа 2011

Это хороший вопрос.Я хотел бы, чтобы больше людей думали о таких вещах.

ИМХО, выбрасывать непроверенные исключения допустимо, если вы передали параметры мусора.

Вообще говоря, вы не должны бросать BadDataException, потому чтоВы не должны использовать исключения для управления потоком программ.Исключения для исключительных.Вызывающие ваш метод могут знать до , что они вызывают его, если их строки - числа или нет, поэтому пропуск мусора можно избежать и поэтому можно считать ошибкой программирования , что означает, что все в порядкевыбрасывать непроверенные исключения.

Относительно объявления throws NumberFormatException - это не так полезно, потому что мало кто заметит из-за того, что NumberFormatException не проверено.Тем не менее, IDE могут использовать его и предлагать правильно обернуть в try/catch.Хорошим вариантом является также использование javadoc, например:

/**
 * Adds two string numbers
 * @param a
 * @param b
 * @return
 * @throws NumberFormatException if either of a or b is not an integer
 */
public static int sum(String a, String b) throws NumberFormatException {
    int x = Integer.parseInt(a); 
    int y = Integer.parseInt(b); 
    return x + y;
}

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

Если метод будет использоваться повсеместно, и важно, чтобы все вызывающие стороны обрабатывали проблемы, объявляйте метод как генерирующий проверенное исключение (заставляющее вызывающие стороны решать проблемы), но загромождающий код с помощью try/catch блоков.

Если, с другой стороны, мы используем этот метод с данными, которым доверяем, то объявите его, как указано выше, потому что он никогда не взорвется, и вы избежите беспорядка кода, по сути ненужного try/catch блоки.

5 голосов
/ 25 августа 2011

Number 4. Как указано, этот метод не должен принимать строки как параметры, он должен принимать целые числа.В этом случае (так как Java-оболочка вместо переполнения) исключение исключено.

 x = sum(Integer.parseInt(a), Integer.parseInt(b))

намного понятнее, что подразумевается под x = sum(a, b)

Вы хотите исключениепроисходить как можно ближе к источнику (входу).

Что касается опций 1-3, вы не определяете исключение, потому что ожидаете, что ваши вызывающие абоненты предполагают, что в противном случае ваш код не сможет завершиться с ошибкой, вы определяете исключение, чтобы определить, что происходит при известных условиях сбоя.УНИКАЛЬНО ДЛЯ ВАШЕГО МЕТОДА.Т.е. если у вас есть метод, который является оберткой вокруг другого объекта, и он выдает исключение, тогда передайте его.Только если исключение является уникальным для вашего метода, вы должны генерировать пользовательское исключение (frex, в вашем примере, если сумма должна была возвращать только положительные результаты, тогда будет уместно проверить это и сгенерировать исключение, если, с другой стороны, javaвыдает исключение переполнения вместо переноса, тогда вы передадите его, а не определите его в своей подписи, переименуете или съедите).

Обновление в ответ на обновление вопроса:

Итак, вкратце: как реализовать семантику СЛЕДУЕТ, если у меня есть только библиотеки MUST.

Решение этой проблемы состоит в том, чтобы обернуть библиотеку MUST и вернуть значение СЛЕДУЕТ.В этом случае функция, которая возвращает целое число.Напишите функцию, которая принимает строку и возвращает объект Integer - либо он работает, либо возвращает null (например, Ints.tryParse в guava).Проведите валидацию отдельно от вашей операции, ваша операция должна занимать целое число.Будет ли ваша операция вызываться со значениями по умолчанию, когда у вас неверный ввод, или вы делаете что-то еще, будет зависеть от ваших спецификаций - большинство, что я могу сказать по этому поводу, это то, что на самом деле маловероятно, что место для принятия этого решения находится в операции.способ.

3 голосов
/ 25 августа 2011

1.Я должен указать NumberFormatException как часть подписи метода.

Я так думаю.Это хорошая документация.

2.Я должен определить свое собственное проверенное исключение (BadDataException), обработать NumberFormatException внутри метода и повторно выдать его как BadDataException.

Иногда да.Проверенные исключения считаются лучшими в некоторых случаях, но работа с ними - это PITA.Вот почему многие фреймворки (например, Hibernate) используют только исключения времени выполнения.

3.Я должен определить свое собственное проверенное исключение (BadDataException), проверить обе строки, например, регулярные выражения, и выдать мое BadDataException, если оно не совпадает.

Никогда.Больше работы, меньше скорости (если вы не ожидаете, что выбрасываете исключение как правило), и вообще никакого выигрыша.

4.Ваша идея?

Нет вообще.

2 голосов
/ 25 августа 2011
  1. Сначала вам нужно спросить себя, нужно ли пользователю моего метода беспокоиться о вводе неправильных данных или от него ожидается ввод правильных данных (в данном случае String). Это ожидание также известно как проект по контракту .

  2. и 3. Да, вы, вероятно, должны определить BadDataException или, что еще лучше, использовать некоторые из них, например NumberFormatException, а не стандартное сообщение, которое нужно показывать. Перехватите NumberFormatException в методе и повторно отправьте его вместе с сообщением, не забывая включить исходную трассировку стека.

  3. Это зависит от ситуации, но я бы, вероятно, пошел с повторным вызовом NumberFormatException с некоторой дополнительной информацией. И также должно быть объяснение javadoc того, что являются ожидаемыми значениями для String a, String b

2 голосов
/ 25 августа 2011

Nr 4.

Думаю, я бы не стал менять метод вообще. Я бы поместил попытку-ловушку вокруг вызывающего метода или выше в трассировку стека, где я нахожусь в контексте, где я могу изящно восстановиться с помощью бизнес-логики из исключения.

Я бы не стал уверенно делать №3, так как считаю это излишним.

2 голосов
/ 25 августа 2011

Предполагая, что то, что вы пишете, будет использовано (например, как API) кем-то другим, тогда вам следует перейти с 1, NumberFormatException специально для передачи таких исключений и должно использоваться.

1 голос
/ 31 августа 2011

Как насчет шаблона проверки ввода, реализованного библиотекой Google 'Guava' или библиотекой Apache 'Validator' ( сравнение )?

По моему опыту, считается хорошей практикой проверять параметры функции в начале функции и генерировать исключения, где это необходимо.

Кроме того, я бы счел этот вопрос в значительной степени независимым от языка. «Хорошая практика» здесь применима ко всем языкам, в которых есть функции, которые могут принимать параметры, которые могут быть или не быть действительными.

1 голос
/ 31 августа 2011

Есть много интересных ответов на этот вопрос.Но я все еще хочу добавить это:

Для разбора строк я всегда предпочитаю использовать «регулярные выражения».Пакет java.util.regex поможет нам.Так что в итоге я получу что-то вроде этого, которое никогда не выдает никаких исключений.Я должен вернуть специальное значение, если я хочу поймать какую-то ошибку:

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public static int sum(String a, String b) {
  final String REGEX = "\\d"; // a single digit
  Pattern pattern = Pattern.compile(REGEX);
  Matcher matcher = pattern.matcher(a);
  if (matcher.find()) { x = Integer.matcher.group(); }
  Matcher matcher = pattern.matcher(b);
  if (matcher.find()) { y = Integer.matcher.group(); }
  return x + y;
}

Как видно, код немного длиннее, но мы можем справиться с тем, что мы хотим (и установить значение по умолчанию).значения для x и y, управляющие тем, что происходит с предложениями else и т. д.). Мы могли бы даже написать более общую подпрограмму преобразования, в которую мы можем передавать строки, возвращать значения по умолчанию, код REGEX для компиляции, сообщения об ошибках для выброса,...

Надеюсь, что это было полезно.

Предупреждение: я не смог протестировать этот код, поэтому прошу прощения за возможные проблемы с синтаксисом.

1 голос
/ 25 августа 2011

Я думаю, что это зависит от вашей цели, но я бы задокументировал это как минимум:

/**
 * @return the sum (as an int) of the two strings
 * @throws NumberFormatException if either string can't be converted to an Integer
 */
public static int sum(String a, String b)
  int x = Integer.parseInt(a);
  int y = Integer.parseInt(b);
  return x + y;
}

Или возьмите страницу из исходного кода Java для класса java.lang.Integer:

public static int parseInt(java.lang.String string) throws java.lang.NumberFormatException;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...