Melhor maneira de retornar sinalizador de status e mensagem de um método em Java

Eu tenho um cenário enganosamente simples, e eu quero uma solução simples, mas não é óbvio que é “mais correto” ou “mais Java”.

Digamos que eu tenha um pequeno método de autenticação (cliente cliente) em alguma class. A autenticação pode falhar por vários motivos e eu quero retornar um booleano simples para o stream de controle, mas também retornar uma mensagem de String para o usuário. Estas são as possibilidades que posso imaginar:

  • Retorna um booleano e passa um StringBuilder para coletar a mensagem. Este é o mais próximo de uma maneira no estilo C.
  • Lance uma exceção em vez de retornar false e inclua a mensagem. Eu não gosto disso, já que o fracasso não é excepcional.
  • Crie uma nova class chamada AuthenticationStatus com o booleano e o String. Isso parece um exagero em um pequeno método.
  • Armazene a mensagem em uma variável de membro. Isso introduziria uma possível condição de corrida, e eu não gosto disso, isso implica algum estado que não está realmente lá.

Alguma outra sugestão?

Editar perdeu esta opção

  • Retornar null para o sucesso – Isso é inseguro?

Editar solução:

Eu fui para a solução mais OO e criei uma pequena class AuthenticationResult. Eu não faria isso em nenhum outro idioma, mas gosto disso em Java. Eu também gostei da sugestão de retornar um String [], já que é como o retorno nulo, mas mais seguro. Uma vantagem da class Result é que você pode ter uma mensagem de sucesso com mais detalhes, se necessário.

Retornar um object pequeno com a flag booleana e a String dentro é provavelmente a maneira mais parecida com o OO, embora eu concorde que parece um exagero para um caso simples como este.

Outra alternativa é sempre retornar uma String, e ter null (ou uma String vazia – você escolhe qual) indica sucesso. Contanto que os valores de retorno sejam claramente explicados nos javadocs, não deve haver confusão alguma.

Você poderia usar exceções ….

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

e, em seguida, se ocorrer um erro no seu AuthenticateMethod, você enviará um novo AuthenticateError (estende a exceção)

Evite retornar um “valor de sentinela”, especialmente nulo. Você vai acabar com uma base de código onde os methods não podem ser compreendidos pelo chamador sem ler a implementação. No caso de null, os chamadores podem acabar com NullPointerExceptions se esquecerem (ou não souberem) que seu método pode retornar null.

A sugestão de tuple de Bas Leijdekkers é uma boa que eu uso o tempo todo se eu quiser retornar mais de um valor de um método. O que usamos é P2 da biblioteca Functional Java . Esse tipo de tipo é uma união conjunta de dois outros tipos (contém um valor de cada tipo).

Lançar exceções para o stream de controle é um pouco um cheiro de código, mas as exceções verificadas são uma maneira de obter mais de um tipo de valor de um método. Outras possibilidades, mais limpas, existem.

  1. Você pode ter uma class abstrata Option com duas subclasss Some e None . Isso é um pouco como uma alternativa segura para o tipo null, e uma boa maneira de implementar funções parciais (funções cujo valor de retorno não é definido para alguns argumentos). A biblioteca Functional Java possui uma class Option com todos os resources que implementa o Iterable , para que você possa fazer algo assim:

     public Option authenticate(String arg) { if (success(arg)) return Option.some("Just an example"); else return Option.none(); } ... for(String s : authenticate(secret)) { privilegedMethod(); } 
  2. Como alternativa, você pode usar uma união disjunta de dois tipos, como uma class Either . Ele contém um valor que é do tipo L ou R Esta class implementa Iterable para L e R , então você pode fazer algo parecido com isto:

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

Todas essas classs, P2 , Option e Either são úteis em uma ampla variedade de situações.

Mais algumas opções:

  • Retornar um valor de enumeração separado para cada tipo de falha. O object enum pode conter a mensagem
  • Retorna um int e tem um método separado que procura a mensagem apropriada de um array
  • crie uma class de tupla de utilitário genérica que possa conter dois valores. Essa class pode ser útil em muitos outros lugares.

exemplo de tupla simples, a implementação real pode precisar de mais:

 class Tuple { public final L left; public final R right; public Tuple( L left, R right) { this.left = left; this.right = right; } } 

Você poderia retornar uma coleção de mensagens de erro, vazia indicando que não houve problemas. Este é um refinamento da sua terceira sugestão.

Eu pessoalmente acho que criar uma nova class chamada AuthenticationStatus com o booleano e a String é a maneira mais Java. E embora pareça um exagero (o que pode ser bem), parece-me mais limpo e mais fácil de entender.

Só porque a autenticação falhada é comum não significa que não seja excepcional.

Na minha opinião, as falhas de autenticação são o caso de uso de pôster-filho para exceções verificadas. (Bem … talvez a inexistência de arquivo seja o caso de uso canônico, mas a falha de autenticação é um # 2 próximo.)

Eu uso a “minúscula class”, geralmente com uma class interna. Eu não gosto de usar argumentos para coletar mensagens.

Além disso, se o método que pode falhar for “baixo nível” – como vindo de um servidor de aplicativos ou da camada de database, prefiro retornar um Enum com o status de retorno e traduzi-lo em uma sequência no nível da GUI. Não divulgue as strings de usuário no nível mais baixo se quiser internacionalizar seu código, pois o servidor do aplicativo só pode responder em um idioma por vez, em vez de ter clientes diferentes trabalhando em idiomas diferentes.

Este é o único método em que você tem essa exigência? Se não, apenas gere uma class Response com um sinalizador isSuccessful e uma string de mensagem e use-a em todos os lugares.

Ou você poderia apenas ter o método retornar null para mostrar sucesso (não é bonito e não permite retornar um sucesso E uma mensagem).

Eu provavelmente iria para algo como:

 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 } 

Com esse “design”, você pode solicitar uma mensagem somente quando a autenticação falhar (o que eu espero que seja o cenário que ocorre 99,99% do tempo;))

Também pode ser uma boa prática delegar a resolução de mensagens para outra Classe. Mas isso depende das necessidades da sua aplicação (principalmente, precisa de i18n?)

Isso parece um idioma comum em outras linguagens de programação, mas não consigo descobrir qual delas (CI adivinhar como li na pergunta).

Quase a mesma pergunta é postada aqui e aqui

A tentativa de retornar dois valores de uma única function pode ser enganosa. Mas, como foi provado pelas tentativas de fazê-lo, pode ser muito útil também.

Definitivamente criar e pequena class com os resultados deve ser a maneira correta de proceder se for um stream comum no aplicativo, conforme publicado anteriormente.

Aqui está uma citação sobre como retornar dois valores de uma function:

Por uma questão de estilo de programação, essa idéia não é atraente em uma linguagem de programação orientada a objects. Retornar objects para representar resultados de computação é a expressão idiomática para retornar vários valores. Alguns sugerem que você não deve ter que declarar classs para valores não relacionados, mas também não devem retornar valores não relacionados de um único método.

Eu encontrei em uma solicitação de recurso para java para permitir vários valores de retorno

veja a secção “avaliação” de: 2005-05-06 09:40:08

Autenticação bem-sucedida deve ser o caso “normal”, portanto, uma falha de autenticação é o caso excepcional.

Quais são as diferentes cadeias de status para o usuário? Eu posso ver apenas dois, sucesso ou fracasso. Qualquer informação adicional é um possível problema de segurança. Outra vantagem da solução com exceções é que ela não pode ser chamada de maneira errada e o caso de falha é mais óbvio. Sem exceções, você escreve:

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

Você pode acidentalmente chamar o método ignorando o valor de retorno. O código “comportamento normal” é então executado sem autenticação bem-sucedida:

 authenticate(); // normal behaviour... 

Se você usar exceções, isso não pode acontecer. Se você decidir não usar exceções, pelo menos, nomeie o método para que fique claro que ele retorna um estado, por exemplo:

 if (isAuthenticated()) { //... } 

Há muitas boas respostas aqui, então vou resumir.

Eu acho que a falha de um usuário para autenticar pode ser considerada um caso válido para uma exceção verificada. Se o seu estilo de programação favorecesse o tratamento de exceções, não haveria razão para não fazer isso. Também remove o “Como retornar vários valores de um método, meu método faz uma coisa: autentica um usuário”

Se você for retornar vários valores, gaste 10 minutos criando um PairTuple genérico (também pode ser mais do que um par TripleTuple, não repetirei o exemplo listado acima) e retorne seus valores dessa maneira. Eu odeio ter pequenos objects de estilo dto para retornar vários valores múltiplos que eles apenas atravancam o lugar.

Que tal retornar uma string? Vazio ou nulo para o sucesso. Mensagem de erro em caso de falha. Mais simples que funcionaria. No entanto, não tenho certeza se lê bem.

Retornar o object. Ele permite que você coloque funcionalidades adicionais na Classe, se necessário. Objetos de curta duração em Java são rápidos para criar e coletar.

Eu escolheria a opção Exception em primeiro lugar.

Mas, em segundo lugar, eu preferiria a técnica do estilo 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; } } 

Isso não é tão estranho e é feito em muitos lugares no framework.

Em vez de criar um object especial para o tipo de retorno, normalmente apenas retorno uma matriz onde todas as informações retornadas são armazenadas. O benefício é que você pode estender esse array com novos elementos sem criar novos tipos e bagunça. A desvantagem é que você precisa saber exatamente quais elementos devem ser apresentados quando a matriz for retornada do método específico para analisá-la corretamente. Geralmente eu concordo com certa estrutura, como o primeiro elemento é sempre o sucesso da indicação booleana, o segundo é String com a descrição, o resto é opcional. Exemplo:

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