Aquí hay un ejemplo muy simplificado . Esta no es necesariamente una pregunta específica del idioma, y le pido que ignore las muchas otras formas en que se puede escribir la función y los cambios que se le pueden hacer. . El color es de un tipo único.
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
else
{
return "No you can't";
}
}
Muchas personas que he conocido, ReSharper, y este tipo (cuyo comentario me recordó que he estado buscando preguntar esto por un tiempo) recomendarían refactorizar el código para eliminar el else
bloqueo dejando esto:
(No recuerdo lo que la mayoría ha dicho, de lo contrario no habría preguntado esto)
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
return "No you can't";
}
Pregunta: ¿Hay un aumento en la complejidad introducido al no incluir el else
bloque?
Tengo la impresión de que la else
intención indica más directamente, al afirmar el hecho de que el código en ambos bloques está directamente relacionado.
Además, encuentro que puedo evitar errores sutiles en la lógica, especialmente después de modificaciones en el código en una fecha posterior.
Tome esta variación de mi ejemplo simplificado (ignorando el hecho del or
operador, ya que este es un ejemplo simplificado a propósito):
bool CanLeaveWithoutUmbrella()
{
if(sky.Color != Color.Blue)
{
return false;
}
return true;
}
Alguien ahora puede agregar un nuevo if
bloque basado en una condición después del primer ejemplo sin reconocer inmediatamente de manera correcta que la primera condición impone una restricción a su propia condición.
Si else
hubiera un bloque presente, quien agregara la nueva condición se vería obligado a mover el contenido del else
bloque (y si de alguna manera lo pasan por alto, la heurística mostrará que el código es inalcanzable, lo que no ocurre en el caso de que uno if
limite a otro) .
Por supuesto, hay otras formas en que el ejemplo específico debe definirse de todos modos, todas las cuales evitan esa situación, pero es solo un ejemplo.
La longitud del ejemplo que di puede sesgar el aspecto visual de esto, así que suponga que el espacio ocupado entre paréntesis es relativamente insignificante para el resto del método.
Olvidé mencionar un caso en el que estoy de acuerdo con la omisión de un bloque else, y es cuando uso un if
bloque para aplicar una restricción que debe cumplirse lógicamente para todos los códigos siguientes, como una verificación nula (o cualquier otra protección) .
fuente
else
cláusula (a mi gusto, será aún más legible al omitir los corchetes innecesarios. Pero creo que el ejemplo no está bien elegido, porque en el en el caso anterior, escribiríareturn sky.Color == Color.Blue
(sin if / else). Un ejemplo con un tipo de retorno diferente a bool probablemente aclararía esto.Respuestas:
En mi opinión, el bloque else explícito es preferible. Cuando veo esto:
Sé que estoy lidiando con opciones mutuamente excluyentes. No necesito leer qué hay dentro de los bloques if para poder contar.
Cuando veo esto:
Parece, a primera vista, que devuelve falso independientemente y tiene un efecto secundario opcional que el cielo no es azul.
Tal como lo veo, la estructura del segundo código no refleja lo que realmente hace el código. Eso es malo. En su lugar, elija la primera opción que refleje la estructura lógica. No quiero tener que verificar la lógica de retorno / interrupción / continuación en cada bloque para conocer el flujo lógico.
Por qué alguien preferiría el otro es un misterio para mí, espero que alguien tome la posición opuesta y me ilumine con su respuesta.
Diría que las condiciones de guardia están bien en el caso de que hayas dejado el camino feliz. Se produjo un error o falla que impide que la ejecución ordinaria continúe. Cuando esto sucede, debe lanzar una excepción, devolver un código de error o lo que sea apropiado para su idioma.
Poco después de la versión original de esta respuesta, se descubrió un código como este en mi proyecto:
Al no poner el retorno dentro de los demás, se pasó por alto el hecho de que faltaba una declaración de retorno. Si se hubiera empleado otra cosa, el código ni siquiera se habría compilado. (Por supuesto, la preocupación mucho mayor que tengo es que las pruebas escritas en este código fueron bastante malas para no detectar este problema).
fuente
else
bloque (puede ser molesto cuando me veo obligado a hacerlo solo para mantenerme en línea con las convenciones para una base de código). Yo también estoy interesado en ver el contrapunto aquí.} // else
lo que normalmente iría el otro como un compromiso entre los dos.La razón principal para eliminar el
else
bloqueo que he encontrado es el sangrado excesivo. El cortocircuito de loselse
bloques permite una estructura de código mucho más plana.Muchas
if
declaraciones son esencialmente guardias. Están evitando la desreferenciación de punteros nulos y otros "¡no hagas esto!" errores Y conducen a una terminación rápida de la función actual. Por ejemplo:Ahora puede parecer que una
else
cláusula sería muy razonable aquí:Y, de hecho, si eso es todo lo que está haciendo, no es mucho más sangrado que el ejemplo anterior. Pero, esto rara vez es todo lo que está haciendo con estructuras de datos reales de varios niveles (una condición que ocurre todo el tiempo cuando se procesan formatos comunes como JSON, HTML y XML). Entonces, si desea modificar el texto de todos los elementos secundarios de un nodo HTML dado, diga:
Los guardias no aumentan la sangría del código. Con una
else
cláusula, todo el trabajo real comienza a moverse hacia la derecha:Esto está comenzando a tener un movimiento significativo hacia la derecha, y este es un ejemplo bastante trivial, con solo dos condiciones de guardia. Cada guardia adicional agrega otro nivel de sangría. El código real que he escrito procesando XML o consumiendo feeds JSON detallados puede acumular fácilmente 3, 4 o más condiciones de protección. Si siempre usa el
else
, termina con sangría de 4, 5 o 6 niveles. Quizás más. Y eso definitivamente contribuye a una sensación de complejidad del código, y más tiempo dedicado a comprender qué se alinea con qué. El estilo rápido, plano y de salida temprana elimina parte de esa "estructura en exceso", y parece más simple.Apéndice Los comentarios sobre esta respuesta me hicieron darme cuenta de que podría no haber sido claro que el manejo NULL / empty no es la única razón para los condicionales de guardia. ¡No cerca! Si bien este ejemplo simple se enfoca en el manejo NULL / empty y no contiene otras razones, la búsqueda de estructuras profundas como XML y AST para contenido "relevante", por ejemplo, a menudo tiene una larga serie de pruebas específicas para eliminar nodos irrelevantes y sin interés casos. Una serie de pruebas de "si este nodo no es relevante, devolver" provocará el mismo tipo de desplazamiento hacia la derecha que los guardias NULL / empty. Y en la práctica, las pruebas de relevancia posteriores se combinan con frecuencia con guardias NULL / vacío para las estructuras de datos correspondientes más profundas buscadas, un doble golpe para la deriva hacia la derecha.
fuente
else
bloque superfluo, cuando uso unif
bloque para aplicar una restricción que debe cumplirse lógicamente para todos los códigos siguientes, como una verificación nula (o cualquier otra protección).then
cláusula (como las sucesivas declaraciones de guardia), entonces no hay un valor particular para evitar laelse
cláusula en particular . De hecho, reduciría la claridad y podría ser peligroso (al no indicar simplemente la estructura alternativa que está / debería estar allí).Cuando veo esto:
Veo un idioma común . Un patrón común , que en mi cabeza se traduce en:
Debido a que este es un idioma muy común en la programación, el programador que está acostumbrado a ver este tipo de código tiene una 'traducción' implícita en su cabeza cada vez que lo ve, que lo 'convierte' al significado adecuado.
No necesito lo explícito
else
, mi cabeza 'lo pone allí' porque reconoció el patrón en su conjunto . Entiendo el significado de todo el patrón común, por lo que no necesito pequeños detalles para aclararlo.Por ejemplo, lea el siguiente texto:
¿Leíste esto?
Si es así, estás equivocado. Lee el texto otra vez. Lo que en realidad está escrito es
Hay dos "las" palabras en el texto. Entonces, ¿por qué te perdiste uno de los dos?
Es porque su cerebro vio un patrón común y lo tradujo inmediatamente a su significado conocido. No fue necesario leer todo detalle por detalle para descubrir el significado, porque ya reconoció el patrón al verlo. Es por eso que ignoró el extra "the ".
Lo mismo con el idioma anterior. Los programadores que han leído mucho código están acostumbrados a ver este patrón , por ejemplo
if(something) {return x;} return y;
. No necesitan un explícitoelse
para entender su significado, porque su cerebro "lo pone allí para ellos". Lo reconocen como un patrón con un significado conocido: "lo que está después de la llave de cierre se ejecutará solo como implícitoelse
del anteriorif
".Entonces, en mi opinión,
else
es innecesario. Pero solo usa lo que parece más legible.fuente
Añaden desorden visual en el código, por lo que sí, podrían agregar complejidad.
Sin embargo, lo contrario también es cierto, la refactorización excesiva para reducir la longitud del código también puede agregar complejidad (no en este caso simple, por supuesto).
Estoy de acuerdo con la afirmación de que el
else
bloque indica la intención. Pero mi conclusión es diferente, porque estás agregando complejidad en tu código para lograr esto.No estoy de acuerdo con su afirmación de que le permite evitar errores sutiles en la lógica.
Veamos un ejemplo, ahora solo debería volver verdadero si el cielo es azul y hay alta humedad, la humedad no es alta o si el cielo es rojo. Pero luego, confundes un aparato ortopédico y lo colocas mal
else
:Hemos visto todo este tipo de errores tontos en el código de producción y puede ser muy difícil de detectar.
Sugeriría lo siguiente:
Esto me resulta más fácil de leer, porque puedo ver de un vistazo que el valor predeterminado es
false
.Además, la idea de obligar a mover el contenido de un bloque para insertar una nueva cláusula es propensa a errores. Esto de alguna manera se relaciona con el principio abierto / cerrado (el código debe estar abierto para la extensión pero cerrado para la modificación). Estás obligando a modificar el código existente (por ejemplo, el codificador podría introducir un error en el equilibrio de llaves).
Finalmente, estás guiando a las personas a responder de una manera predeterminada que crees que es la correcta. Por ejemplo, presenta un ejemplo muy simple pero luego declara que las personas no deben tomarlo en consideración. Sin embargo, la simplicidad del ejemplo afecta toda la pregunta:
Si el caso es tan simple como el presentado, entonces mi respuesta sería omitir cualquier
if else
cláusula.return (sky.Color == Color.Blue);
Si el caso es un poco más complicado, con varias cláusulas, respondería:
bool CanLeaveWithoutUmbrella () {
}
Si el caso es complicado y no todas las cláusulas devuelven verdadero (tal vez devuelven un doble), y quiero introducir algún punto de interrupción o comando de inicio de sesión, consideraría algo como:
}
Si no estamos hablando de un método que devuelve algo, sino un bloque if-else que establece alguna variable o llama a un método, entonces sí, incluiría una
else
cláusula final .Por lo tanto, la simplicidad del ejemplo también es un factor a considerar.
fuente
Aunque el caso if-else descrito tiene la mayor complejidad, en la mayoría (pero no en todas) las situaciones prácticas, no importa mucho.
Hace años, cuando estaba trabajando en un software que se utilizaba para la industria de la aviación (tenía que pasar por un proceso de certificación), surgió una pregunta suya. Resultó que había un costo financiero en esa declaración de "otra cosa", ya que aumentaba la complejidad del código.
Este es el por qué.
La presencia del 'else' creó un 3er caso implícito que tuvo que ser evaluado, sin tomar el 'if' ni el 'else'. Usted podría estar pensando (como yo estaba en ese momento) WTF?
La explicación fue así ...
Desde un punto de vista lógico, solo hay dos opciones: 'if' y 'else'. Sin embargo, lo que estábamos certificando era el archivo objeto que el compilador estaba generando. Por lo tanto, cuando había un 'else', se tenía que hacer un análisis para confirmar que el código de ramificación generado era correcto y que no aparecía un misterioso tercer camino.
Compare esto con el caso donde solo hay un 'si' y no 'otro'. En ese caso, solo hay dos opciones: la ruta 'if' y la ruta 'non-if'. Dos casos para evaluar son menos complejos que tres.
Espero que esto ayude.
fuente
El objetivo más importante del código es ser comprensible como comprometido (en lugar de refactorizarse fácilmente, lo cual es útil pero menos importante). Se puede usar un ejemplo de Python un poco más complejo para ilustrar esto:
Esto es bastante claro: es el equivalente de una
case
declaración o búsqueda de diccionario, la versión de nivel superior dereturn foo == bar
:Esto es más claro, porque aunque el número de tokens es mayor (32 frente a 24 para el código original con dos pares clave / valor), la semántica de la función ahora es explícita: es solo una búsqueda.
(Esto tiene consecuencias para la refactorización: si desea tener un efecto secundario si
key == NIK
tiene tres opciones:if
/elif
/else
e inserte una sola línea en elkey == NIK
caso.if
declaración avalue
para el caso especial antes de regresar.if
declaración en la persona que llama.Ahora vemos por qué la función de búsqueda es una simplificación poderosa: hace que la tercera opción sea obviamente la solución más simple, ya que además de dar como resultado una diferencia menor que la primera opción , es la única que se asegura de que
value
solo haga una cosa.)Volviendo al código de OP, creo que esto puede servir como guía para la complejidad de las
else
declaraciones: el código con unaelse
declaración explícita es objetivamente más complejo, pero más importante es si hace que la semántica del código sea clara y simple. Y eso tiene que ser respondido caso por caso, ya que cada parte del código tiene un significado diferente.fuente
case
comoswitch
declaraciones) son menos homogéneos. Varias opciones son solo retornos de valor directo, pero uno o dos casos necesitan un procesamiento adicional. Y cuando descubra que la estrategia de búsqueda no es adecuada, debe volver a escribir en unaif
elif
...else
sintaxis completamente diferente .Creo que depende de qué tipo de
if
declaración estés usando. Algunosif
declaraciones pueden verse como expresiones, y otras solo pueden verse como declaraciones de flujo de control.Tu ejemplo me parece una expresión. En lenguajes tipo C, el operador ternario
?:
es muy parecido a unaif
declaración, pero es una expresión, y la parte else no puede omitirse. Tiene sentido que una rama else no se pueda omitir en una expresión, porque la expresión siempre debe tener un valor.Usando el operador ternario, su ejemplo se vería así:
Sin embargo, dado que es una expresión booleana, realmente debería simplificarse completamente para
La inclusión de una cláusula else explícita aclara su intención. Los guardias pueden tener sentido en algunas situaciones, pero al seleccionar entre dos valores posibles, ninguno de los cuales es excepcional o inusual, no ayudan.
fuente
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Otra cosa a tener en cuenta en este argumento son los hábitos que promueve cada enfoque.
if / else reformula una muestra de codificación en términos de ramas. Como dices, a veces esta explicidad es buena. Realmente hay dos posibles rutas de ejecución y queremos resaltar eso.
En otros casos, solo hay una ruta de ejecución y luego todas esas cosas excepcionales de las que los programadores deben preocuparse. Como mencionó, las cláusulas de guardia son excelentes para esto.
Existe, por supuesto, el caso 3 o más (n), donde generalmente alentamos a abandonar la cadena if / else y usar una declaración case / switch o polimorfismo.
El principio rector que tengo en cuenta aquí es que un método o función debe tener un solo propósito. Cualquier código con una declaración if / else o switch es solo para bifurcación. Por lo tanto, debe ser absurdamente corto (4 líneas) y solo delegar en el método correcto o producir el resultado obvio (como su ejemplo). Cuando su código es tan corto, es difícil perder esos retornos múltiples :) Al igual que "goto considerado dañino", se puede abusar de cualquier declaración de ramificación, por lo que generalmente vale la pena pensar un poco en las declaraciones de lo contrario. Como mencionó, solo tener un bloque else proporciona un lugar para que el código se agrupe. Usted y su equipo tendrán que decidir cómo se siente al respecto.
De lo que realmente se queja la herramienta es del hecho de que tiene un retorno / descanso / lanzamiento dentro del bloque if. Entonces, en lo que a él respecta, escribiste una cláusula de guardia pero la arruinaste. Puede elegir hacer feliz la herramienta o puede "entretener" su sugerencia sin aceptarla.
fuente
else
semánticamente, las herramientas de análisis de código generalmente buscan instrucciones extrañas porque a menudo resaltan un malentendido del flujo de control. Elelse
after anif
que regresa es bits desperdiciados.if(1 == 1)
a menudo provoca el mismo tipo de advertencia.Creo que la respuesta es que depende. Su código de muestra (como señala indirectamente) simplemente devuelve la evaluación de una condición, y se representa mejor como obviamente sabe, como una sola expresión.
Para determinar si agregar una condición else aclara u oculta el código, debe determinar qué representa IF / ELSE. ¿Es una (como en su ejemplo) una expresión? Si es así, esa expresión debe extraerse y usarse. ¿Es una condición de guardia? Si es así, entonces ELSE es innecesario y engañoso, ya que hace que las dos ramas parezcan equivalentes. ¿Es un ejemplo de polimorfismo procesal? Extraelo. ¿Está estableciendo condiciones previas? Entonces puede ser esencial.
Antes de decidir incluir o eliminar el ELSE, decida qué representa, eso le dirá si debe estar presente o no.
fuente
La respuesta es un poco subjetiva. Un
else
bloque puede ayudar a la legibilidad al establecer que un bloque de código se ejecuta exclusivamente en otro bloque de código, sin necesidad de leer ambos bloques. Esto puede ser útil si, por ejemplo, ambos bloques son relativamente largos. Hay casos, aunque tenerif
s sinelse
s puede ser más legible. Compare el siguiente código con variosif/else
bloques:con:
El segundo bloque de código cambia las
if
declaraciones anidadas en cláusulas de protección. Esto reduce la sangría y aclara algunas de las condiciones de retorno ("¡sia != b
, entonces volvemos0
!")Se ha señalado en los comentarios que puede haber algunos agujeros en mi ejemplo, por lo que voy a profundizar un poco más.
Podríamos haber escrito el primer bloque de código aquí para hacerlo más legible:
Este código es equivalente a los dos bloques anteriores, pero presenta algunos desafíos. La
else
cláusula aquí conecta estas declaraciones if juntas. En la versión de la cláusula de protección anterior, las cláusulas de protección son independientes entre sí. Agregar uno nuevo significa simplemente escribir uno nuevoif
entre el últimoif
y el resto de la lógica. Aquí, eso también se puede hacer, con solo una pequeña cantidad más de carga cognitiva. Sin embargo, la diferencia es cuando se necesita una lógica adicional antes de esa nueva cláusula de protección. Cuando las declaraciones fueron independientes, pudimos hacer:Con el conectado
if/else
cadena , esto no es tan fácil de hacer. De hecho, el camino más fácil sería romper la cadena para parecerse más a la versión de la cláusula de guardia.Pero realmente, esto tiene sentido. Usar
if/else
débilmente implica una relación entre las partes. Podríamos suponer quea != b
está relacionado de alguna manerac > d
en laif/else
versión encadenada. Esa suposición sería engañosa, como podemos ver en la versión de la cláusula de guardia que, de hecho, no están relacionados. Eso significa que podemos hacer más con cláusulas independientes, como reorganizarlas (tal vez para optimizar el rendimiento de la consulta o para mejorar el flujo lógico). También podemos ignorarlos cognitivamente una vez que los hayamos pasado. En unaif/else
cadena, estoy menos seguro de que la relación de equivalencia entrea
yb
no volverá a aparecer más tarde.La relación implicada por
else
también es evidente en el código de ejemplo profundamente anidado, pero de una manera diferente. Laselse
declaraciones están separadas del condicional al que están unidas, y están relacionadas en orden inverso a las condicionales (la primeraelse
está unida a la últimaif
, la últimaelse
está unida a la primeraif
). Esto crea una disonancia cognitiva en la que tenemos que retroceder a través del código, tratando de alinear la sangría en nuestros cerebros. Es un poco como tratar de combinar un par de paréntesis. Se pone aún peor si la sangría está mal. Los frenos opcionales tampoco ayudan.Esta es una buena concesión, pero realmente no invalida ninguna respuesta. Podría decir eso en su ejemplo de código:
Una interpretación válida para escribir código como este podría ser que "solo debemos irnos con un paraguas si el cielo no es azul". Aquí hay una restricción de que el cielo debe ser azul para que el código siguiente sea válido para ejecutarse. He conocido la definición de tu concesión.
¿Qué pasa si digo que si el color del cielo es negro, es una noche clara, por lo que no es necesario un paraguas? (Hay formas más simples de escribir esto, pero hay formas más simples de escribir todo el ejemplo).
Como en el ejemplo más grande anterior, simplemente puedo agregar la nueva cláusula sin pensarlo mucho. En un
if/else
, no puedo estar tan seguro de no estropear algo, especialmente si la lógica es más complicada.También señalaré que su ejemplo simple tampoco es tan simple. Una prueba para un valor no binario no es lo mismo que la inversa de esa prueba con resultados invertidos.
fuente
else
bloque es esencial para la legibilidad.Has simplificado demasiado tu ejemplo. Cualquiera de las alternativas puede ser más legible, dependiendo de la situación más amplia: específicamente, depende de si una de las dos ramas de la condición es anormal . Déjame mostrarte: en un caso donde cualquiera de las ramas es igualmente probable, ...
... es más legible que ...
... porque el primero exhibe una estructura paralela y el segundo no. Pero, cuando la mayor parte de la función se dedica a una tarea y solo necesita verificar algunas condiciones previas primero, ...
}
... es más legible que ...
... porque deliberadamente no establecer una estructura paralela proporciona una pista para un futuro lector de que obtener una entrada que "realmente escriba dos" aquí es anormal . (En la vida real,
ProcessInputDefinitelyOfTypeOne
no sería una función separada, por lo que la diferencia sería aún más marcada).fuente
if
-> Hacer lo excepcional y dejar« como una estrategia de retorno temprano . Piense en el código, en el que el valor predeterminado incluye más verificaciones, sería más rápido manejar el caso excepcional primero.when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards)
. Lógicamente, cualquier trabajoProcessInputOfTypeOne
depende del hecho de que!InputIsReallyTypeTwo(input)
, por lo tanto, necesitamos detectarlo fuera de nuestro procesamiento normal. NormalmenteProcessInputOfTypeTwo(input);
sería realmentethrow ICan'tProccessThatException
lo que haría más evidente la similitud.u+
" generalmente introduce un token de "rango unicode", pero si el siguiente carácter no es un dígito hexadecimal, entonces uno necesita hacer una copia de seguridad y procesar la 'u' como un identificador en su lugar. Entonces, el bucle principal del escáner se despacha para "escanear un token de rango unicode" de manera algo imprecisa, y comienza con "... si estamos en este caso especial inusual, haga una copia de seguridad, luego llame a la subrutina de escanear un identificador".ProcessInputOfTypeOne
utiliza una verificación imprecisa pero rápida que a veces capturas tipo dos en su lugar.Sí, hay un aumento en la complejidad porque la cláusula else es redundante . Por lo tanto, es mejor dejar de lado para mantener el código delgado y malo. Está en el mismo acorde que omitir
this
calificadores redundantes (Java, C ++, C #) y omitir paréntesis en lasreturn
declaraciones, y así sucesivamente.Un buen programador piensa "¿qué puedo agregar?" mientras que un gran programador piensa "¿qué puedo eliminar?".
fuente
else
bloque, si se explica de una sola vez (lo que no es frecuente) a menudo es con este tipo de razonamiento, entonces veo +1 para presentar el argumento "predeterminado", pero no lo hago. encuentra que es un razonamiento lo suficientemente fuerte.A good programmer things "what can I add?" while a great programmer things "what can I remove?".
parece ser un adagio común de que muchas personas se extienden en exceso sin una cuidadosa consideración, y personalmente considero que este es uno de esos casos.Me di cuenta, cambiando de opinión sobre este caso.
Cuando me hicieron esta pregunta hace aproximadamente un año, habría respondido algo como:
»Debería haber solo uno
entry
y unoexit
para un método« Y con esto en mente, habría sugerido refactorizar el código para:Desde el punto de vista de la comunicación, está claro qué se debe hacer.
If
algo es el caso, manipular el resultado de una manera ,else
manipularlo de otra manera .Pero esto implica una innecesaria
else
. Elelse
expresa el caso predeterminado . Entonces, en un segundo paso, podría refactorizarlo paraEntonces surge la pregunta: ¿Por qué usar una variable temporal? El siguiente paso de refactorización conduce a:
Entonces esto está claro en lo que hace. Incluso iría un paso más allá:
O para los débiles de corazón :
Podrías leerlo así: »CanLeaveWithoutUmbrella devuelve un bool . Si no sucede nada especial, devuelve falso «Esta es la primera lectura, de arriba a abajo.
Y luego "analiza" la condición »Si se cumple la condición, devuelve verdadero « Podría omitir por completo el condicional y haber entendido lo que hace esta función en el caso predeterminado . Tu ojo y tu mente solo tienen que leer algunas letras en el lado izquierdo, sin leer la parte de la derecha.
Si, eso es correcto. Pero esa información es redundante . Tiene un caso predeterminado y una "excepción" que está cubierta por la
if
declaración.Al tratar con estructuras más complejas, elegiría otra estrategia, omitiendo el
if
o es el hermano feoswitch
en absoluto.fuente
So this is clear in what it does...
y expandirlo? (Los casos anteriores también son de relevancia cuestionable). Digo esto porque es lo que está preguntando la pregunta que examinamos. Has declarado tu postura, omitiendo el bloque else, y en realidad es la postura la que necesita más explicación (y la que me hizo hacer esta pregunta). De la pregunta:I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Para resumir, en este caso, deshacerse de la
else
palabra clave es algo bueno. Aquí es totalmente redundante y, dependiendo de cómo formatee su código, agregará una o tres líneas más completamente inútiles a su código. Considere lo que hay en elif
bloque:Por lo tanto, si
if
se ingresara el bloque, el resto de la función, por definición, no se ejecuta. Pero si no se ingresa, ya que no hay másif
declaraciones, se ejecuta el resto de la función. Sería totalmente diferente si unareturn
declaración no estuviera en elif
bloque, en cuyo caso la presencia o ausencia de unelse
bloque tendría un impacto, pero aquí, no tendría un impacto.En su pregunta, después de invertir su
if
condición y omitirlaelse
, declaró lo siguiente:Necesitan leer el código un poco más de cerca entonces. Utilizamos lenguajes de alto nivel y una organización clara del código para mitigar estos problemas, pero agregar un
else
bloque completamente redundante agrega "ruido" y "desorden" al código, y tampoco deberíamos tener que invertir o reinvertir nuestrasif
condiciones. Si no pueden notar la diferencia, entonces no saben lo que están haciendo. Si pueden notar la diferencia, siempre pueden invertir el originalif
condiciones ellos mismos.Sin embargo, eso es esencialmente lo que está sucediendo aquí. Está volviendo del
if
bloque, por lo que laif
condición de evaluaciónfalse
es una restricción que debe cumplirse lógicamente para todos los códigos siguientes.No, constituirá una disminución en la complejidad de su código, e incluso puede constituir una disminución en el código compilado, dependiendo de si se realizarán o no ciertas optimizaciones súper triviales.
fuente
En el ejemplo específico que diste, lo hace.
Creo que no podría ser más simple que esto:
fuente
if
declaración. De hecho, desaconsejo explícitamente una mayor simplificación utilizando el código exacto que utilizó. Además, la complejidad ciclomática no aumenta con elelse
bloque.Siempre trato de escribir mi código de manera que la intención sea lo más clara posible. Su ejemplo carece de contexto, así que lo modificaré un poco:
Nuestra intención parece ser que podemos irnos sin paraguas cuando el cielo es azul. Sin embargo, esa simple intención se pierde en un mar de código. Hay varias formas en que podemos facilitar su comprensión.
En primer lugar, está su pregunta sobre la
else
sucursal. No me importa de ninguna manera, pero tiendo a dejar de ladoelse
. Entiendo el argumento acerca de ser explícito, pero mi opinión es que lasif
declaraciones deben envolver ramas 'opcionales', como lareturn true
. No llamaría alreturn false
rama 'opcional', ya que actúa como nuestro valor predeterminado. De cualquier manera, veo esto como un problema muy menor y mucho menos importante que los siguientes:¡Una cuestión mucho más importante es que sus sucursales están haciendo demasiado! Las ramas, como todo, deberían ser "lo más simple posible, pero no más". En este caso, ha utilizado una
if
declaración, que se bifurca entre bloques de código (colecciones de declaraciones) cuando todo lo que realmente necesita para bifurcarse son expresiones (valores). Esto está claro ya que a) sus bloques de código solo contienen declaraciones únicas yb) son la misma declaración (return
). Podemos usar el operador ternario?:
para reducir la rama solo a la parte que es diferente:Ahora llegamos a la redundancia obvia a la que nuestro resultado es idéntico
this.color == Color.Blue
. Sé que su pregunta nos pide que ignoremos esto, pero creo que es realmente importante señalar que esta es una forma de pensar de primer orden y procedimiento: está desglosando los valores de entrada y construyendo algunos valores de salida. Eso está bien, pero si es posible es mucho más poderoso pensar en términos de relaciones o transformaciones entre la entrada y la salida. En este caso, la relación es obviamente que son iguales, pero a menudo veo un código de procedimiento complicado como este, que oculta alguna relación simple. Avancemos y hagamos esta simplificación:Lo siguiente que señalaría es que su API contiene un doble negativo: es posible
CanLeaveWithoutUmbrella
que lo seafalse
. Esto, por supuesto, se simplifica aNeedUmbrella
sertrue
, así que hagamos eso:Ahora podemos comenzar a pensar en su estilo de codificación. Parece que está usando un lenguaje orientado a objetos, pero al usar
if
declaraciones para ramificarse entre bloques de código, ha terminado escribiendo procedimientos código de .En el código orientado a objetos, todos los bloques de código son métodos y todas las ramificaciones se realizan a través del despacho dinámico , por ejemplo. usando clases o prototipos. Es discutible si eso simplifica las cosas, pero debería hacer que su código sea más coherente con el resto del lenguaje:
Tenga en cuenta que el uso de operadores ternarios para ramificar entre expresiones es común en la programación funcional .
fuente
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
. De hecho, usa la línea exacta de código que menciono. Además, aunque esta parte de la pregunta está fuera de tema, diría que está yendo demasiado lejos en su inferencia. Has cambiado radicalmente el código.return false;
vselse { return false; }
mientras no preocuparse por la polaridad rama, envío dinámico, ternarios, etc. Si estás escribiendo código de procedimientos en un lenguaje orientado a objetos, el cambio radical es necesario;)if/else
declaración, y qué es la polaridad de la rama.