valor - sentencia return java
La mejor forma de devolver el indicador de estado y el mensaje desde un método en Java (17)
¿Es este el único método donde tienes ese requerimiento? Si no, solo genere una clase de respuesta general con un indicador isSuccessful y una cadena de mensaje, y use eso en todas partes.
O simplemente puede hacer que el método devuelva nulo para mostrar el éxito (no es bonito, y no permite devolver un mensaje Y éxito).
Tengo un escenario engañosamente simple, y quiero una solución simple, pero no es obvio cuál es "más correcto" o "más Java".
Digamos que tengo un pequeño método de autenticación (Cliente cliente) en alguna clase. La autenticación puede fallar por una serie de razones, y quiero devolver un booleano simple para el flujo de control, pero también devolver un mensaje de cadena para el usuario. Estas son las posibilidades que se me ocurren:
- Devuelve un valor booleano y pasa un StringBuilder para recopilar el mensaje. Esto es lo más parecido a una forma de hacerlo al estilo C
- Lanza una excepción en lugar de devolver falsa, e incluye el mensaje. No me gusta esto ya que el fracaso no es excepcional.
- Cree una nueva clase llamada AuthenticationStatus con boolean y String. Esto parece exagerado por un pequeño método.
- Almacene el mensaje en una variable miembro. Esto introduciría una posible condición de carrera, y no me gusta que implique algún estado que no esté realmente allí.
¿Cualquier otra sugerencia?
Editar Perdió esta opción
- Devuelve nulo para tener éxito. ¿Esto no es seguro?
Edición de solución:
Fui a buscar la solución más OO y creé una pequeña clase de AutenticaciónResultada. No haría esto en ningún otro idioma, pero me gusta en Java. También me gustó la sugerencia de devolver un String [] ya que es como el retorno nulo pero más seguro. Una ventaja de la clase Result es que puede obtener un mensaje de éxito con más detalles si es necesario.
¿Qué hay de devolver una cadena? Vacío o nulo para tener éxito. Mensaje de error en caso de falla. Lo más simple que funcionaría. Sin embargo, no estoy seguro si se lee bien.
Algunas más opciones:
- Devuelve un valor de enumeración por separado para cada tipo de falla. El objeto enum podría contener el mensaje
- Devuelve un int y tiene un método separado que busca el mensaje apropiado de una matriz
- crear una clase de tupla de utilidad genérica que puede contener dos valores. Tal clase puede ser útil en muchos más lugares.
ejemplo de tupla simple, la implementación real puede necesitar más:
class Tuple<L, R> {
public final L left;
public final R right;
public Tuple( L left, R right) {
this.left = left;
this.right = right;
}
}
Devolver un pequeño objeto con el indicador booleano y el String inside es probablemente la forma más similar a OO de hacerlo, aunque estoy de acuerdo en que parece excesivo para un caso simple como este.
Otra alternativa es devolver siempre una Cadena, y tener nulo (o una Cadena vacía - eliges cuál) indican el éxito. Siempre que los valores de retorno estén claramente explicados en los javadocs no debería haber confusión.
Devuelve el objeto. Le permite agregar funcionalidad adicional a la Clase si la necesita. Los objetos de vida corta en Java son rápidos de crear y recopilar.
El hecho de que la autenticación fallida sea un lugar común no significa que no sea excepcional.
En mi opinión, las fallas de autenticación son el caso de uso de poster-child para excepciones marcadas. (Bueno ... tal vez la no existencia de archivos es el caso de uso canónico, pero la falla de autenticación es un cierre n. ° 2).
En lugar de crear un objeto especial para el tipo de devolución, generalmente solo devuelvo una matriz donde se almacena toda la información devuelta. El beneficio es que puede ampliar esta matriz con nuevos elementos sin crear nuevos tipos y desorden. La desventaja es que debes saber exactamente qué elementos deben aparecer cuando se devuelve una matriz de un método particular para analizarla correctamente. Por lo general, estoy de acuerdo con cierta estructura, como el primer elemento es siempre el éxito de la indicación booleana, el segundo es Cadena con descripción, el resto es opcional. Ejemplo:
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;
}
Escogería la opción Excepción en primer lugar.
Pero, en segundo lugar, preferiría la técnica de 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;
}
}
Esto no es tan extraño y se hace en muchos lugares en el marco.
Evite devolver un "valor centinela", especialmente nulo. Usted terminará con una base de código donde los métodos no pueden ser entendidos por la persona que llama sin leer la implementación. En el caso de nulo, las personas que llaman pueden terminar con NullPointerExceptions si olvidan (o no saben) que su método puede devolver nulo.
La sugerencia de tupla de Bas Leijdekkers es buena y la uso todo el tiempo si deseo devolver más de un valor de un método. El que usamos es P2<A, B>
de la biblioteca funcional de Java . Este tipo de tipo es una unión conjunta de otros dos tipos (contiene un valor de cada tipo).
Lanzar Excepciones para el flujo de control es un poco olor a código, pero las excepciones comprobadas son una forma de obtener más de un tipo de valor de un método. Sin embargo, existen otras posibilidades más limpias.
Puede tener una clase abstracta
Option<T>
con dos subclasesSome<T>
yNone<T>
. Esto es un poco como una alternativa segura de tipo a nulo, y una buena forma de implementar funciones parciales (funciones cuyo valor de retorno no está definido para algunos argumentos). La biblioteca funcional de Java tiene una clase deOption
con todas las funciones que implementaIterable<T>
, por lo que puede hacer algo como esto: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(); }
Alternativamente, puede usar una unión disjunta de dos tipos, como una clase
Either<L, R>
. Contiene un valor que es del tipoL
oR
Esta clase implementaIterable<T>
paraL
yR
, por lo que puede hacer algo como esto: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"); }
Todas estas clases, P2
, Option
y Either
son útiles en una amplia variedad de situaciones.
Hay muchas buenas respuestas aquí, así que lo mantendré corto.
Creo que la falla de un usuario para autenticarse puede considerarse un caso válido para una excepción marcada. Si su estilo de programación favoreció el manejo de excepciones, entonces no habría razón para no hacer esto. También elimina el "Cómo devolver múltiples valores de un método, mi método hace una cosa. Autentica a un usuario"
Si va a devolver varios valores, pase 10 minutos creando un PairTuple genérico (también puede ser más de un par TripleTuple, no repetiré el ejemplo mencionado anteriormente) y devuelva sus valores de esa manera. Odio tener pequeños objetos de estilo dto para devolver varios valores múltiples que simplemente desordenan el lugar.
La autenticación exitosa debe ser el caso "normal", por lo que una falla de autenticación es el caso excepcional.
¿Cuáles son las diferentes cadenas de estado para el usuario de todos modos. Solo puedo ver dos, éxito o fracaso. Cualquier información adicional es un problema potencial de seguridad. Otra ventaja de la solución con excepciones es que no se puede invocar de manera incorrecta y el caso de falla es más obvio. Sin excepciones, escribes:
if (authenticate()) {
// normal behaviour...
}
else {
// error case...
}
Puede llamar accidentalmente al método ignorando el valor de retorno. El código de "comportamiento normal" se ejecuta luego sin autenticación exitosa:
authenticate();
// normal behaviour...
Si usa excepciones, eso no puede suceder. Si decide no usar excepciones, al menos asigne un nombre al método para que quede claro que devuelve un estado, por ejemplo:
if (isAuthenticated()) {
//...
}
Personalmente, creo que crear una nueva clase llamada AuthenticationStatus con boolean y String es la forma más similar a Java. Y si bien parece excesivo (que bien puede ser), me parece más limpio y más fácil de entender.
Podría devolver una Colección de mensajes de error, vacía indicando que no hubo problemas. Este es un refinamiento de su tercera sugerencia.
Probablemente iría por 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
}
Con este "diseño", puede solicitar un mensaje solo cuando falla la autenticación (que espero que sea el escenario que ocurre el 99,99% del tiempo;))
También puede ser una buena práctica delegar la resolución del mensaje a otra clase. Pero depende de las necesidades de su aplicación (principalmente, ¿necesita i18n?)
Puedes usar excepciones ....
try {
AuthenticateMethod();
} catch (AuthenticateError ae) {
// Display ae.getMessage() to user..
System.out.println(ae.getMessage());
//ae.printStackTrace();
}
y luego, si ocurre un error en su AuthenticateMethod, usted envía un nuevo AuthenticateError (extends Exception)
Yo uso la "pequeña clase" yo mismo, generalmente con una clase interna. No me gusta usar argumentos para recopilar mensajes.
Además, si el método que puede fallar es de "bajo nivel", como si proviniera de un servidor de aplicaciones o de la capa de la base de datos, preferiría devolver un Enum con el estado de retorno y luego traducirlo a una cadena en el nivel de la GUI. No pase las secuencias de usuario en el nivel bajo si alguna vez va a internacionalizar su código, porque entonces su servidor de aplicaciones solo puede responder en un idioma a la vez, en lugar de tener diferentes clientes trabajando en diferentes idiomas.
Esto parece una expresión común en otros lenguajes de programación, pero no puedo determinar cuál (supongo que DC como leo en la pregunta).
Casi la misma pregunta se publica aquí y aquí
Intentar devolver dos valores de una sola función puede ser engañoso. Pero como ha sido probado por los intentos de hacerlo, también puede ser muy útil.
Definitivamente crear y una clase pequeña con los resultados debe ser la forma correcta de proceder si ese es un flujo común en la aplicación como se publicó anteriormente.
Aquí hay una cita sobre devolver dos valores de una función:
Como una cuestión de estilo de programación, esta idea no es atractiva en un lenguaje de programación orientado a objetos. Devolver objetos para representar resultados de cálculo es la expresión idiomática para devolver múltiples valores. Algunos sugieren que no debería tener que declarar clases para valores no relacionados, pero tampoco deberían devolverse valores no relacionados desde un único método.
Lo he encontrado en una solicitud de función para que java permita múltiples valores de devolución
mira la sección de "evaluación" fecha: 2005-05-06 09:40:08