java - ¿Por qué el "else" rara vez se usa después de "si x luego regresa"?
coding-style if-statement (23)
Este método:
boolean containsSmiley(String s) {
if (s == null) {
return false;
}
else {
return s.contains(":)");
}
}
se puede escribir de manera equivalente:
boolean containsSmiley(String s) {
if (s == null) {
return false;
}
return s.contains(":)");
}
En mi experiencia, la segunda forma se ve con más frecuencia, especialmente en métodos más complejos (donde puede haber varios de esos puntos de salida), y lo mismo es cierto para "arrojar" como para "regresar". Sin embargo, la primera forma posiblemente hace que la estructura condicional del código sea más explícita. ¿Hay alguna razón para preferir uno sobre el otro?
(Relacionado: ¿Debería una función tener solo una declaración de retorno? )
Al igual que cualquier "discusión" sobre estilos de codificación, no hay una respuesta correcta. Prefiero aplicar estas consideraciones:
¿El código funciona como se espera en todas las situaciones? (Principio de menos sorpresa)
¿Puede el próximo desarrollador (yo o alguien más) entender lo que está haciendo y por qué?
Qué tan frágil es el código con respecto al cambio.
Es tan simple como debe ser y no más. Es decir, no sobre o bajo ingeniería.
Una vez que estoy contento de haber satisfecho lo anterior, el resto generalmente solo cae en línea.
Bueno, algunas de las razones son solo convenciones, pero hay una ventaja en el formulario anterior ...
Es típico cuando se codifica una declaración de devolución, para hacer que la última declaración sea su valor de retorno predeterminado. Esto ayuda principalmente durante la refactorización: las cláusulas else tienden a quedar envueltas en otras estructuras o pueden accidentalmente moverse más adentro del árbol.
Como puede ver, diferentes personas tienen diferentes opiniones sobre la legibilidad. Algunas personas piensan que menos líneas de código tienden a hacer que el código sea más legible. Otros piensan que la simetría de la segunda forma la hace más legible.
Mi opinión es que, probablemente, ambos puntos de vista son correctos ... para las personas que los tienen. Y el corolario es que no se puede escribir código que todos encuentren legible de manera óptima. Entonces, el mejor consejo es seguir lo que su estándar de codificación obligatorio dice que hacer (si dice algo al respecto) y generalmente usar su sentido común. (Si está agobiado por algún idiota vociferante que insiste en que su camino es "correcto" ... solo siga la corriente).
Discutiría por la legibilidad. Si está escaneando pantallas de código tratando de descubrir qué hace el código, es un aviso visual para el desarrollador.
... pero no es realmente necesario porque todos comentamos nuestro código tan bien, ¿verdad? :)
El else
en ese caso sería redundante, así como también crear indentaciones adicionales innecesarias para el código principal de la función.
El else
es redundante. Además, algunos IDE (Eclipse) y herramientas de análisis (tal vez FindBugs) pueden señalarlo como una advertencia o un error, por lo que en ese caso los programadores probablemente lo eliminen.
En mi experiencia, depende del código. Si estoy ''vigilando'' algo, lo haré:
if (inputVar.isBad()) {
return;
}
doThings();
El punto es claro: si esa afirmación es falsa, no quiero que la función continúe.
Por otro lado, hay algunas funciones con múltiples opciones, y en ese caso lo escribiría así:
if (inputVar == thingOne) {
doFirstThing();
} else if (inputVar == secondThing) {
doSecondThing();
} else {
doThirdThing();
}
Aunque podría escribirse como:
if (inputVar == thingOne) {
doFirstThing();
return;
}
if (inputVar == thingTwo) {
doSecondThing();
return;
}
doThingThree();
return;
Realmente se trata de qué manera muestra más claramente lo que está haciendo el código (no necesariamente qué bit de código es el más corto o tiene la menor sangría).
En mi opinión, el segundo tiene más sentido. Sirve más como una acción ''predeterminada'', como un interruptor. Si no coincide con ninguno de los otros puntos de salida, entonces hazlo. Realmente no necesitas otro allí. Diría que si toda la función es solo if y elseif, entonces un else tendría sentido allí porque es un condicional gigante. Si hay varios condicionales y otras funciones que se ejecutan dentro de él, entonces se usará un retorno predeterminado al final.
Es un argumento religioso y al final del día no importa. Incluso diría que la primera forma es más legible en algunas circunstancias. Si tiene grandes fragmentos de código en if-elseif-elseif-else
, es más fácil, a primera vista, ver cuál es la devolución predeterminada.
if (s == null) {
return false;
}
else if (s.Contains(":))")) {
return true;
}
else if (s.Contains(":-(")) {
return false;
}
return s.contains(":)");
Este es un patrón llamado Guard Clause . La idea es hacer todo el control por adelantado para reducir las condiciones anidadas y aumentar la legibilidad.
Desde el enlace:
double getPayAmount() {
double result;
if (_isDead) {
result = deadAmount();
} else {
if (_isSeparated) {
result = separatedAmount();
} else {
if (_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}
Usando la cláusula Guard, podrás ver este resultado:
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
La declaración if
es verificar / hacer cumplir su contrato / expectativa de no recibir valores nulos. Por esa razón, preferiría verlo separado del resto de la función, ya que no tiene nada que ver con la lógica real de lo que estás tratando de lograr (aunque este caso es muy simple).
Sin embargo, en la mayoría de los casos, prefiero que el código sea lo más explícito posible en su intento. Si hay algo que pueda reestructurar sobre su función para que sea más legible para otros, hágalo. Como programador profesional, su objetivo realmente debería ser programar para aquellos que tienen que mantener su código después de usted (incluyéndose a usted dos años después ...). Cualquier cosa que puedas hacer para ayudarlos vale la pena.
La primera forma es simplemente menos detallada: cuando devuelve un valor, abandona automáticamente el alcance de la función en la que se encuentra y lo devuelve a la persona que llama, por lo que cualquiera de los códigos posteriores solo se ejecutará si la instrucción IF no se evalúa como verdadera y luego devuelve algo.
La segunda forma si es más simple / más corta. Esto no siempre significa más claro. Te sugiero que hagas lo que encuentres más claro. Personalmente escribiría.
static boolean containsSmiley(String s) {
return s != null && s.contains(":)");
}
Porque es lo mismo que escribir uno de los siguientes que trae la evidencia sobre la intención del programador:
boolean containsSmiley(String s) {
if (s == null) // The curly braces are not even necessary as the if contains only one instruction.
return false;
return s.contains(":)");
}
O incluso esto:
boolean constainsSMiley(String s) {
return string.IsNullOrEmpty(s) ? false : s.Contains(":)");
}
Estas dos formas son:
- Mas elegante;
- Más fácil de leer;
- Más ligero y más rápido para el programador de lectura.
Porque es más agradable. Sabes que también puedes usar ''{'' ''}'' para crear varios niveles de anidación, pero nadie realmente lo hace por el simple hecho de hacerlo.
Porque hay una advertencia opcional (apagado por defecto) en eclipse si else se usa en tal situación;).
Preferiría la primera opción, ya que es más humanamente legible.
Como analogía, compare las siguientes 2 oraciones: "Si hoy está lloviendo, entonces tome un paraguas, de lo contrario, tome gafas de sol". y "Si hoy está lloviendo, toma un paraguas, lleva gafas de sol". La primera oración corresponde al primer bloque de código de la pregunta, el segundo a la segunda. El primero es mucho más claro y legible, ¿no?
Preferiría un punto de salida sobre el múltiple desde la perspectiva de mantenimiento. El resultado final se puede modificar (o decorar) en un punto de salida en lugar de en n puntos de salida.
Prefiero escribirlo así:
boolean containsSmiley(String s) {
return s != null && s.contains(":)");
}
Probablemente, alguien más haya notado esto, pero recomendaría no usar valores nulos en general, donde se esperan cadenas. Si realmente quiere un control para evitar que alguien pase valores nulos, puede usar los asertos (en tiempo de desarrollo) o las pruebas unitarias (implementar):
boolean containsSmiley(String s) {
assert s != null : "Quit passing null values, you moron.";
return s.contains(":)");
}
He cambiado a una regla general: nunca. Nunca. pase valores nulos, a menos que una API externa llame explícitamente. Segundo: si un método externo puede devolver valores nulos, reemplácelo con un valor no nulo razonable (como una cadena vacía) o agregue una verificación ordenada. Me canso de verificaciones repetitivas if (thing == null)
.
Pero eso es un poco offtopic. Me gusta poner condiciones cortas en las cláusulas de arriba y de guardia, eliminando los puntos si el flujo del programa indica que nunca llegarán allí.
Si bien tener un else es correcto y no hay nada de malo en términos de lógica y capacidad de ejecución, me gusta evitar el momento WTF inicial cuando la función no tiene declaración de devolución fuera del alcance if / else.
Verás todo esto:
if (condition) {
return var;
}
// by nature, when execution reaches this point, condition can only be false,
// therefore, the else is unnecessary
return other_var;
La mayoría de las veces, la adición de una cláusula else no solo es innecesaria en este caso, sino que muchas veces, el compilador la optimiza .
Piense en cómo la computadora piensa en este código (en términos de código de máquina, simplificado en pseudocódigo aquí para fines de demostración):
0x00: test [condition]
0x01: if result of test was not true, goto [0x04]
0x02: push [var] onto stack
0x03: goto [0x05]
0x04: push [other_var] onto stack
0x05: return from subroutine
El código (nuevamente, este es un pseudocódigo y no un conjunto) actuaría de la misma manera para un condicional if/then/else
.
Para muchas personas, se considera una práctica mala y / o confusa tener múltiples puntos de salida posibles para una función, ya que un programador debe pensar en todas las rutas posibles a través de su código. Otra práctica es la siguiente:
return (condition) ? var : other_var;
Esto simplifica el código y no crea ningún punto de salida nuevo.
La Navaja de Occam es el principio de que "las entidades no deben multiplicarse más allá de la necesidad".