Лучший способ вернуть флаг состояния и сообщение из метода в Java

У меня обманчиво простой сценарий, и я хочу простое решение, но не очевидно, что является «наиболее правильным» или «большинством Java».

Допустим, у меня есть небольшой метод аутентификации (клиент-клиент) в каком-то классе. Аутентификация может быть неудачной по ряду причин, и я хочу вернуть простое логическое значение для потока управления, но также вернуть сообщение String для пользователя. Вот возможности, о которых я могу думать:

  • Верните логическое значение и передайте StringBuilder для сбора сообщения. Это самый близкий к C-стилю способ сделать это.
  • Выведите исключение вместо возврата false и включите сообщение. Мне это не нравится, так как неудача не является исключительной.
  • Создайте новый класс с именем AuthenticationStatus с логическим значением и строкой. Это кажется излишним для одного маленького метода.
  • Сохраните сообщение в переменной-члене. Это может привести к потенциальному состоянию гонки, и мне не нравится, что это подразумевает некое состояние, которого на самом деле нет.

Любые другие предложения?

Редактировать Пропущена эта опция отключена

  • Вернуть ноль для успеха - это небезопасно?

Изменить решение:

Я выбрал самое решение ОО и создал небольшой класс AuthenticationResult. Я бы не стал делать это ни на каком другом языке, но мне нравится это на Java. Мне также понравилось предложение вернуть String [], так как это похоже на нулевой возврат, но безопаснее. Одним из преимуществ класса Result является то, что вы можете получить сообщение об успехе с дополнительной информацией, если это необходимо.

10.12.2008 14:32:31
Не могли бы вы объяснить, почему это больше ОО, чем возвращение дозорного значения? Я прохожу ту же ситуацию и использую ваше решение. Но я не знаю, почему это решение лучше, чем другие.
Rumel 25.09.2016 05:06:10
@Rumel, я думаю, что другие ответы и комментарии объясняют это довольно хорошо, но в двух словах значение дозорного опасно, потому что оно использует лазейку в системе типов (значение, которое вы можете вернуть для этого типа, которое вы не ожидаете ни в каком другом другое обстоятельство). Использование NULL особенно опасно, поскольку пропущенные проверки NULL просты и распространены, что может привести к ошибкам. Это больше ОО, потому что он использует класс, который определенно определяет тип, который означает «Результат аутентификации» или «Состояние с дополнительными сообщениями», поэтому нет злоупотребления типом за счет более тяжелого типа.
Draemon 7.10.2016 16:56:26
17 ОТВЕТОВ
РЕШЕНИЕ

Возврат небольшого объекта с булевым флагом и String внутри, вероятно, является наиболее ОО-подобным способом сделать это, хотя я согласен, что в таком простом случае, как этот, это кажется излишним.

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

13
10.12.2008 14:36:26
Возврат пустой строки или нуля для обозначения сбоя называется возвращением «значения часового». Ни один из них не является особенно безопасным, поскольку вы эффективно побеждаете систему типов.
Apocalisp 10.12.2008 15:17:48
Ребята, проблема здесь в том, что возвращаемые значения часто и легко игнорируются. Следовательно, с чем-то вроде аутентификации маршрут исключения является более безопасным.
Nick Holt 10.12.2008 15:35:25
Ноль - плохая идея, потому что в какой-то момент вы НЕ будете этого ожидать.
Guillaume 10.12.2008 16:47:46
Sentinels - это особенно плохая идея, если вы требуете, чтобы вызывающий абонент вручную проверял возвращаемое значение, чтобы обеспечить целостность вашей системы аутентификации. Когда учетные данные пользователя не проходят проверку подлинности, логика приложения должна полностью сойти с рельсов, что делает невозможным дальнейшую работу. Полная остановка.
benjismith 10.12.2008 18:07:54
Я твердо убежден, что возвращать null из метода - плохая практика, потому что мне надоело иметь дело с хромыми исключениями NullPointerException в устаревшем коде. Пожалуйста, примите во внимание следующее: c2.com/cgi/wiki?NoNullBeyondMethodScope
Kyle W. Cartmell 15.10.2009 21:01:19

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

1
10.12.2008 14:35:18
Я фактически использую это в другом месте в приложении, но здесь никогда не будет> 1 сообщения. Тем не менее, пустой массив может быть хорошим индикатором успеха.
Draemon 10.12.2008 14:42:01
Каждый раз, когда вы думаете «индикатор успеха», меняйте его на «индикатор неудачи» и используйте исключение ;-)
Guillaume 10.12.2008 16:38:10

Как насчет возврата строки. Пусто или Нуль для успеха. Сообщение об ошибке в случае сбоя. Самое простое, что будет работать. Однако не уверен, что он хорошо читается.

-1
10.12.2008 14:36:54

Я лично думаю, что создание нового класса с именем AuthenticationStatus с логическим значением и String - это наиболее похожий на Java способ. И хотя это кажется излишним (что вполне может быть), оно кажется мне чище и легче для понимания.

1
10.12.2008 14:37:00
Определение класса с четырьмя строками не кажется мне небольшим излишним значением :-)
James Anderson 10.12.2008 14:41:35
Это становится излишним, когда вы продолжаете переделывать этот же файл, только немного различаясь для каждого приложения, тогда как, возможно, разработчик Y решит не использовать его в других приложениях. Кроме того, он создает больше раздувания в определенных средах сборки EAR, в которых могут быть дублированные, но неравные POJO.
Zombies 10.12.2008 18:42:03

Я использую «крошечный класс» сам, обычно с внутренним классом. Мне не нравится использовать аргументы для сбора сообщений.

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

0
10.12.2008 14:37:50
Справедливо по поводу сообщений, но здесь это не важно. Я должен был дать понять, что это сообщение системного уровня, которое будет использоваться только для целей отладки / ведения журнала. Это на самом деле не видно пользователю.
Draemon 10.12.2008 14:44:03

Это единственный метод, когда у вас есть такое требование? Если нет, просто сгенерируйте общий класс Response с флагом isSuccessful и строкой сообщения и используйте его везде.

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

0
10.12.2008 14:39:24

Вы можете использовать исключения ....

try {
    AuthenticateMethod();
} catch (AuthenticateError ae) {         
    // Display ae.getMessage() to user..
    System.out.println(ae.getMessage());
    //ae.printStackTrace();    
}

и затем, если в вашем AuthenticateMethod возникает ошибка, вы отправляете новый AuthenticateError (расширяет исключение)

13
10.12.2008 14:40:15
Я согласен с этим. Ошибка аутентификации - это не ошибка, которую вы хотите, чтобы вызывающая сторона могла легко игнорировать.
jmucchiello 10.12.2008 14:43:24
Я тоже согласен Может быть, это не исключительный случай для приложения, но это определенно исключительный случай для этой «подсистемы» соединения. Вот почему я бы посоветовал не только использовать исключение, но и создавать разные для разных типов сбоев.
bezmax 10.12.2008 14:53:08
Никогда не используйте исключения для реализации бизнес-логики.
Peter 10.12.2008 14:57:13
@Peter ... Афоризм, который вы ищете: «Никогда не используйте исключения для реализации управления потоком». Это не контроль потока. Пользователь предоставляет учетные данные, ожидающие входа в систему, но учетные данные не проходят проверку подлинности. Почему? Возможно, такого имени пользователя не существует. Возможно, срок действия пароля истек. Кто знает?
benjismith 10.12.2008 18:03:33
Аутентификация соответствует предоставленным учетным данным для пользователя. Есть два (общих) ожидаемых результата: совпадение найдено и совпадение не найдено. Бизнес-логика аутентификации должна сигнализировать остальной части приложения, что делать в любом случае. Известные способы, которыми аутентификация не могла "успешно" быть обработана с нормальным потоком управления; неизвестные способы могут быть обработаны с исключениями (например, невозможно подключиться к базовой подсистеме).
antony.trupe 12.09.2013 02:47:17

Еще несколько вариантов:

  • Вернуть отдельное значение перечисления для каждого типа ошибки. Объект enum может содержать сообщение
  • Вернуть int и иметь отдельный метод, который ищет соответствующее сообщение из массива
  • создать универсальный класс кортежа утилит, который может содержать два значения. Такой класс может быть полезен во многих других местах.

Простой пример кортежа, фактической реализации может потребоваться больше:

class Tuple<L, R> {

    public final L left;
    public final R right;

    public Tuple( L left, R right) {
        this.left = left;
        this.right = right;
    }
}
3
10.12.2008 14:42:24
Я согласен с тем, что PairTuples - это очень полезные классы, они избавляют от необходимости создавать все эти беспорядочные DTO, которые тусуются вокруг. Я бы переопределил equals, hashcode и toString, и его можно использовать где угодно, в коллекциях и т. Д.
Shawn Vader 10.12.2008 18:10:33
1) Перечисления + функции декодера <файлы декларативных свойств 2) Как насчет 3 значений? или 4? ... какое значение какое? Разве это не просто набор ценностей?
Zombies 10.12.2008 18:36:44

Я бы выбрал вариант исключения в первую очередь.

Но, во-вторых, я бы предпочел технику в стиле C:

public boolean authenticate(Client client, final StringBuilder sb) {
    if (sb == null)
        throw new IllegalArgumentException();
    if (isOK()) {
        sb.append("info message");
        return true;
    } else {
        sb.append("error message");
        return false;
    }
}

Это не так странно, и это делается во многих местах в рамках.

-1
10.12.2008 14:48:46

Вместо создания специального объекта для возвращаемого типа я обычно просто возвращаю массив, в котором хранится вся возвращаемая информация. Преимущество заключается в том, что вы можете расширить этот массив новыми элементами, не создавая новых типов и беспорядка. Недостатком является то, что вы должны точно знать, какие элементы должны присутствовать при возврате массива из определенного метода, чтобы правильно его проанализировать. Обычно я согласен с определенной структурой, так как первый элемент всегда имеет логическое указание на успех, второй - это строка с описанием, остальное необязательно. Пример:

public static void main(String[] args)
{
    Object[] result = methodReturningStatus();
    if(!(Boolean)result[0])
        System.out.println("Method return: "+ result[1]);
}

static Object[] methodReturningStatus()
{
    Object[] result = new Object[2];

    result[0] = false;
    result[1] = "Error happened";

    return result;
}
-2
10.12.2008 14:50:13

Скорее всего, я бы пошел на что-то вроде:


class SomeClass {
public int authenticate (Client client) {
//returns 0 if success otherwise one value per possible failure
}
public String getAuthenticationResultMessage (int authenticateResult) {}
//returns message associated to authenticateResult
}

При таком «дизайне» вы можете запросить сообщение только в случае сбоя аутентификации (что, я надеюсь, является сценарием, который происходит в 99,99% времени;))

Также может быть полезно делегировать разрешение сообщений другому классу. Но это зависит от потребностей вашего приложения (в основном, нужен ли i18n?)

0
10.12.2008 14:58:35
Это как решение enum, но с целочисленными значениями. Перечисления будут более java-y
Draemon 10.12.2008 15:03:32

Избегайте возврата «дозорного значения», особенно нулевого. В итоге вы получите кодовую базу, в которой вызывающая сторона не сможет понять методы без чтения реализации. В случае нулевого значения вызывающие могут получить исключение NullPointerException, если они забудут (или не знают), что ваш метод может вернуть нулевое значение.

Предложение кортежа от Bas Leijdekkers - это хороший вариант, который я использую все время, если хочу вернуть более одного значения из метода. Тот, который мы используем, взят P2<A, B>из функциональной библиотеки Java . Этот тип типа является объединением двух других типов (он содержит одно значение каждого типа).

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

  1. Вы можете иметь Option<T>абстрактный класс с двумя подклассами Some<T>и None<T>. Это немного похоже на безопасную альтернативу null и является хорошим способом реализации частичных функций (функций, возвращаемое значение которых не определено для некоторых аргументов). Функциональная Java библиотека имеет полнофункциональный Optionкласс , который реализует Iterable<T>, так что вы можете сделать что - то вроде этого:

    public Option<String> authenticate(String arg) {
       if (success(arg))
          return Option.some("Just an example");
       else
          return Option.none();
    }
    
    ...
    
    for(String s : authenticate(secret)) {
       privilegedMethod();
    }
  2. В качестве альтернативы вы можете использовать несвязанное объединение двух типов в качестве Either<L, R>класса. Он содержит одно значение, которое имеет тип Lили R. Этот класс реализует Iterable<T>для обоих Lи R, так что вы можете сделать что-то вроде этого:

    public Either<Fail, String> authenticate(String arg) {
       if (success(arg))
          return Either.right("Just an example");
       else
          return Either.left(Fail.authenticationFailure());
    }
    
    ...
    
    Either<Fail, String> auth = authenticate(secret);
    for(String s : auth.rightProjection()) {
       privilegedMethod();
    }
    for(Fail f : auth.leftProjection()) {
       System.out.println("FAIL");
    }

Все эти классы, P2, Optionи Eitherмогут быть использованы в самых различных ситуациях.

8
10.12.2008 19:25:50
Java8 имеет опцию Optional, которая также обеспечивает функциональность, описанную в (1.).
Maikefer 31.05.2016 12:19:59

Тот факт, что неудачная аутентификация является обычным явлением, не означает, что она не является исключительной.

На мой взгляд, ошибки аутентификации - это случай использования дочернего элемента для проверенных исключений. (Ну ... может быть, каноническое использование - это несуществование файла, но ошибка аутентификации близка # 2.)

1
10.12.2008 15:34:29
Есть загвоздка с исключениями. Они не сочиняют. Например, если вы хотите аутентифицировать целый список пользователей и собирать сбои, необходимость отловить исключения делает это излишне трудным.
Apocalisp 10.12.2008 18:03:27
Интересно. Какой вариант использования для одновременной аутентификации нескольких пользователей?
benjismith 10.12.2008 18:19:32
Даже если для этого есть сценарий использования, обработка аутентификаций в пакете подразумевает, что они ВСЕ должны либо успешно завершиться, либо потерпеть неудачу, как пакет (в этом случае исключения по-прежнему являются лучшим вариантом). В противном случае, я бы сказал, что вы не работаете в пакетном режиме, и у каждого автора должна быть своя попытка / отлов.
benjismith 10.12.2008 18:25:53
Это может быть тест, или, может быть, вы выполняете фоновую работу от имени нескольких пользователей. Принцип применим к любому количеству вариантов использования, многие из которых могут не включать списки. Возможно, вы захотите передать модуль авторизации, который действует по-разному для аутентифицированных и не прошедших проверку пользователей.
Apocalisp 10.12.2008 18:33:58

Верните объект. Это позволяет вам добавить дополнительные функциональные возможности в класс, если вам это нужно. Краткосрочные объекты в Java быстро создаются и собираются.

-1
10.12.2008 16:21:56

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

Почти один и тот же вопрос размещен здесь и здесь

Попытка вернуть два значения из одной функции может вводить в заблуждение. Но, как показали попытки сделать это, это может быть очень полезно.

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

Вот цитата о возврате двух значений из функции:

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

Я нашел его в запросе функции для Java, чтобы разрешить несколько возвращаемых значений

посмотрите раздел "оценка" от: 2005-05-06 09:40:08

0
23.05.2017 12:02:11

Успешная аутентификация должна быть «нормальным» случаем, поэтому ошибка аутентификации является исключительным случаем.

Каковы различные строки статуса для пользователя в любом случае. Я вижу только два, успех или неудачу. Любая дополнительная информация является потенциальной проблемой безопасности. Другое преимущество решения с исключениями состоит в том, что оно не может быть вызвано неправильно, и случай сбоя более очевиден. Без исключений, вы пишете:

if (authenticate()) {
  // normal behaviour...
}
else {
  // error case...
}

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

authenticate();
// normal behaviour...

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

if (isAuthenticated()) {
//...
}
0
10.12.2008 17:26:40

Здесь много хороших ответов, поэтому я буду кратким.

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

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

0
10.12.2008 18:04:58