what the statements statement sirve que programming para examples else are programming-languages if-statement

programming-languages - statements - what are the conditional operators?



¿Por qué la afirmación ''si'' se considera malvada? (18)

Acabo de venir de Simple Design and Testing Conference . En una de las sesiones hablamos sobre malas palabras clave en los lenguajes de programación. Corey Haines , quien propuso el tema, estaba convencido de que if declaración es absolutamente malvada. Su alternativa era crear funciones con predicates . ¿Puedes explicarme por qué if es malvado?

Entiendo que puedes escribir código muy feo abusando de if . Pero no creo que sea tan malo.


A veces es necesario tomar una posición extrema para expresar su punto. Estoy seguro de que esta persona usa if , pero cada vez que usa un if , vale la pena pensar un poco si un patrón diferente aclarará el código.

Preferir el polimorfismo a if está en el núcleo de esto. Más bien que:

if(animaltype = bird) { squawk(); } else if(animaltype = dog) { bark(); }

... utilizar:

animal.makeSound();

Pero eso supone que tienes una clase / interfaz Animal, así que realmente lo que te está diciendo es que necesitas crear esa interfaz.

Entonces, en el mundo real, ¿qué tipo de if vemos que nos lleve a una solución de polimorfismo?

if(logging) { log.write("Did something"); }

Eso es realmente irritante de ver en todo tu código. ¿Qué tal, en cambio, tener dos (o más) implementaciones de Logger?

this.logger = new NullLogger(); // logger.log() does nothing this.logger = new StdOutLogger(); // logger.log() writes to stdout

Eso nos lleva al patrón de estrategia .

En lugar de:

if(user.getCreditRisk() > 50) { decision = thoroughCreditCheck(); } else if(user.getCreditRisk() > 20) { decision = mediumCreditCheck(); } else { decision = cursoryCreditCheck(); }

... podrías tener ...

decision = getCreditCheckStrategy(user.getCreditRisk()).decide();

Por supuesto, getCreditCheckStrategy() podría contener un if , y eso podría ser apropiado. Lo has empujado a un lugar ordenado donde pertenece.


Al igual que en el versículo bíblico sobre el dinero, si las declaraciones no son malas, el AMOR de las declaraciones si es malo. Un programa sin declaraciones if es una idea ridícula, y usarlas como sea necesario es esencial. Pero un programa que tiene 100 if-else si bloquea en una fila (que, lamentablemente, he visto) es definitivamente malo.


Bien que en ruby tenemos a menos que ;)

Pero seriamente probablemente, si es el próximo goto , que incluso si la mayoría de la gente piensa que es malo en algunos casos es simplificar / acelerar las cosas (y en algunos casos, como el código de alto nivel optimizado es imprescindible).


Creo que depende de lo que estás haciendo para ser honesto.

Si tiene una declaración if..else simple, ¿por qué usar un predicate ?

Si puede, use un switch para reemplazos más grandes, y luego si la opción de usar un predicado para operaciones grandes (donde tenga sentido, de lo contrario su código será una pesadilla para mantener), úselo.

Este tipo parece haber sido un poco pedante para mi gusto. Reemplazar todo if está en Predicates es solo una locura.


Creo que si las declaraciones son malas, pero si las expresiones no. Lo que quiero decir con una expresión if en este caso puede ser algo así como el operador ternario de C # (condición? TrueExpression: falseExpression). Esto no es malo porque es una función pura (en un sentido matemático). Se evalúa a un nuevo valor, pero no tiene ningún efecto sobre ninguna otra cosa. Debido a esto, funciona en un modelo de sustitución.

Imperativo Si las declaraciones son malas porque te obligan a crear efectos secundarios cuando no es necesario. Para que una declaración If sea significativa, debe producir diferentes "efectos" según la expresión de condición. Estos efectos pueden ser cosas como IO, representación gráfica o transacciones de bases de datos, que cambian las cosas fuera del programa. O bien, podrían ser instrucciones de asignación que modifiquen el estado de las variables existentes. Por lo general, es mejor minimizar estos efectos y separarlos de la lógica real. Pero, debido a las declaraciones If, podemos agregar libremente estos "efectos condicionalmente ejecutados" en todas partes del código. Creo que eso es malo


Debo decir que recientemente comencé a ver si las declaraciones como un código huelen: especialmente cuando te encuentras repitiendo la misma condición varias veces. Pero hay algo que debes entender sobre los olores de código: no necesariamente significan que el código es malo. Solo quieren decir que hay una buena posibilidad de que el código sea malo.

Por ejemplo, los comentarios se enumeran como un olor a código por Martin Fowler, pero no tomaría en serio a nadie que diga "los comentarios son malos, no los use".

Sin embargo, en general, prefiero usar polimorfismo en lugar de declaraciones si es posible. Eso solo significa mucho menos margen de error. Tiendo a encontrar que una gran cantidad de tiempo, el uso de condicionales conduce a una gran cantidad de argumentos de vagabundo también (porque tiene que pasar los datos necesarios para formar el condicional al método apropiado).


El único problema con los predicados (en términos de reemplazar sentencias if ) es que aún necesita probarlos:

function void Test(Predicate<int> pr, int num) { if (pr(num)) { /* do something */ } else { /* do something else */ } }

Por supuesto, podría usar el operador de ternario ( ?: :), Pero eso es solo una declaración de if en disfraz ...


Estaré de acuerdo contigo él estaba equivocado Puedes ir demasiado lejos con cosas así, demasiado inteligente para tu propio bien.

El código creado con predicados en lugar de ifs sería horrendo para mantener y probar.


Existe la campaña Anti-If que comenzó a principios de año. La premisa principal es que muchas declaraciones anidadas con frecuencia a menudo pueden ser reemplazadas por polimorfismo.

Me gustaría ver un ejemplo del uso del predicado en su lugar. ¿Es esto más como una programación funcional?


Hay otro sentido en el que puede ser malo: cuando se trata de polimorfismo.

P.ej

if (animal.isFrog()) croak(animal) else if (animal.isDog()) bark(animal) else if (animal.isLion()) roar(animal)

en lugar de

animal.emitSound()

Pero, básicamente, si es una herramienta perfectamente aceptable para lo que hace. Se puede abusar y mal usar, por supuesto, pero no está cerca del estado de goto.


Las cláusulas condicionales a veces dan como resultado un código que es más difícil de administrar. Esto no solo incluye la instrucción if , sino aún más comúnmente la instrucción switch , que generalmente incluye más ramas que un if correspondiente.

Hay casos en los que es perfectamente razonable usar un

Cuando está escribiendo métodos de utilidad, extensiones o funciones de biblioteca específicas, es probable que no pueda evitarlo if s (y no debería). No hay una mejor manera de codificar esta pequeña función, ni hacerla más auto-documentada de lo que es:

// this is a good "if" use-case int Min(int a, int b) { if (a < b) return a; else return b; } // or, if you prefer the ternary operator int Min(int a, int b) { return (a < b) ? a : b; }

La ramificación sobre un "código de tipo" es un olor a código

Por otro lado, si encuentra un código que prueba algún tipo de código, o prueba si una variable es de cierto tipo, entonces es muy probable que sea un buen candidato para refactorizar, es decir, reemplazar el condicional por polimorfismo .

La razón de esto es que al permitir que sus interlocutores ramifiquen en un cierto tipo de código, está creando la posibilidad de terminar con numerosos controles diseminados por todo su código, lo que hace que las extensiones y el mantenimiento sean mucho más complejos. El polimorfismo, por otro lado, le permite llevar esta decisión de ramificación lo más cerca posible de la raíz de su programa.

Considerar:

// this is called branching on a "type code", // and screams for refactoring void RunVehicle(Vehicle vehicle) { // how the hell do I even test this? if (vehicle.Type == CAR) Drive(vehicle); else if (vehicle.Type == PLANE) Fly(vehicle); else Sail(vehicle); }

Al colocar la funcionalidad común pero específica del tipo (es decir, específica de la clase) en clases separadas y exponerla a través de un método virtual (o una interfaz), permite que las partes internas de su programa deleguen esta decisión a alguien superior en la jerarquía de llamadas ( potencialmente en un solo lugar en el código), permitiendo pruebas mucho más fáciles (burla), extensibilidad y mantenimiento:

// adding a new vehicle is gonna be a piece of cake interface IVehicle { void Run(); } // your method now doesn''t care about which vehicle // it got as a parameter void RunVehicle(IVehicle vehicle) { vehicle.Run(); }

Y ahora puede probar fácilmente si su método RunVehicle funciona como debería:

// you can now create test (mock) implementations // since you''re passing it as an interface var mock = new Mock<IVehicle>(); // run the client method something.RunVehicle(mock.Object); // check if Run() was invoked mock.Verify(m => m.Run(), Times.Once());

Los patrones que solo difieren en sus condiciones if se pueden reutilizar

Con respecto al argumento sobre la sustitución if con un "predicado" en su pregunta, Haines probablemente quería mencionar que a veces existen patrones similares sobre su código, que difieren solo en sus expresiones condicionales. Las expresiones condicionales surgen junto con if s, pero la idea general es extraer un patrón repetitivo en un método separado, dejando la expresión como un parámetro. Esto es lo que ya hace LINQ, generalmente resulta en un código más limpio en comparación con un foreach alternativo:

Considere estos dos métodos muy similares:

// average male age public double AverageMaleAge(List<Person> people) { double sum = 0.0; foreach (var person in people) { if (person.Gender == Gender.Male) sum += person.Age; } return sum / people.Count; } // average female age public double AverageFemaleAge(List<Person> people) { double sum = 0.0; foreach (var person in people) { if (person.Gender == Gender.Female) // <-- only the expression sum += person.Age; // is different } return sum / people.Count; }

Esto indica que puede extraer la condición en un predicado, dejándole un único método para estos dos casos (y muchos otros casos futuros):

// average age for all people matched by the predicate public double AverageAge(List<Person> people, Predicate<Person> match) { double sum = 0.0; foreach (var person in people) { if (match(person)) // <-- the decision to match sum += person.Age; // is now delegated to callers } return sum / people.Count; } var males = AverageAge(people, p => p.Gender == Gender.Male); var females = AverageAge(people, p => p.Gender == Gender.Female);

Y como LINQ ya tiene muchos métodos de extensión útiles como este, en realidad ni siquiera necesita escribir sus propios métodos:

// replace everything we''ve written above with these two lines var males = list.Where(p => p.Gender == Gender.Male).Average(p => p.Age); var females = list.Where(p => p.Gender == Gender.Female).Average(p => p.Age);

En esta última versión de LINQ, la instrucción if ha "desaparecido" por completo, aunque:

  1. para ser sincero, el problema no estaba en el if por sí mismo, sino en todo el patrón de código (simplemente porque estaba duplicado), y
  2. el if todavía existe, pero está escrito dentro del método de extensión LINQ Where , que se ha probado y cerrado para su modificación. Tener menos de tu propio código siempre es algo bueno: menos cosas para probar, menos cosas que salen mal, y el código es más fácil de seguir, analizar y mantener.

Refactoriza cuando sientas que huele a código, pero no sobreescribas

Habiendo dicho todo esto, no deberías pasar noches sin dormir por tener un par de condicionales de vez en cuando. Si bien estas respuestas pueden proporcionar algunas reglas generales, la mejor manera de detectar construcciones que necesitan refactorización es a través de la experiencia. Con el tiempo, surgen algunos patrones que resultan en la modificación de las mismas cláusulas una y otra vez.


Los predicados provienen de lenguajes de programación lógicos / declarativos, como PROLOG. Para ciertas clases de problemas, como la resolución de restricciones, son discutiblemente superiores a una gran cantidad de paso a paso si-esto-qué-hacer-hacer-esta mierda. Los problemas que serían largos y complejos de resolver en idiomas imperativos se pueden hacer en unas pocas líneas en PROLOG.

También está el problema de la programación escalable (debido al movimiento hacia multinúcleo, la web, etc.). Si las declaraciones y la programación imperativa en general tienden a estar en orden paso a paso, y no son escalables. Sin embargo, las declaraciones lógicas y el cálculo lambda describen cómo se puede resolver un problema y en qué partes se puede descomponer. Como resultado, el intérprete / procesador que ejecuta ese código puede dividir el código de manera eficiente y dividirlo en varias CPU / núcleos / hilos / servidores.

Definitivamente no es útil en todas partes; Odiaría intentar escribir un controlador de dispositivo con predicados en lugar de declaraciones de if. Pero sí, creo que el punto principal es probablemente el sonido, y al menos vale la pena familiarizarse con él, si no se usa todo el tiempo.


OMI: Sospecho que estaba tratando de provocar un debate y hacer que la gente piense sobre el mal uso del "si". Nadie sugeriría seriamente que una construcción tan fundamental de la sintaxis de programación se evitara por completo, ¿verdad?


Probablemente se deba al deseo de reducir la complejidad ciclomática del código y reducir el número de puntos de ramificación en una función. Si una función es simple de descomponer en una serie de funciones más pequeñas, cada una de las cuales puede ser probada, puede reducir la complejidad y hacer que el código sea más fácilmente comprobable.


Quizás con la computación cuántica será una estrategia sensata no usar declaraciones IF, sino permitir que cada parte del cálculo proceda y solo tenga la función ''colapsar'' en la terminación para obtener un resultado útil.


Si no es malvado! Considerar ...

int sum(int a, int b) { return a + b; }

Aburrido, ¿eh? Ahora con un agregado si ...

int sum(int a, int b) { if (a == 0 && b == 0) { return 0; } return a + b; }

... la productividad de creación de código (medida en LOC) se duplica.

También la legibilidad del código ha mejorado mucho, por ahora se puede ver en un abrir y cerrar de ojos cuál es el resultado cuando ambos argumentos son cero. No podrías hacer eso en el código de arriba, ¿verdad?

Además, usted apoyó al equipo de pruebas, ya que ahora pueden impulsar su código. Las herramientas de prueba de cobertura se agotan más hasta los límites.

Además, el código ahora está mejor preparado para futuras mejoras. Supongamos, por ejemplo, que la suma debe ser cero si uno de los argumentos es cero (no se ría y no me culpe, tontos requisitos del cliente, ya sabe, y el cliente siempre tiene la razón). Debido a que si en primer lugar solo se necesita un ligero cambio de código.

int sum(int a, int b) { if (a == 0 || b == 0) { return 0; } return a + b; }

¿Cuánto más cambio de código habría necesitado si no hubiera inventado el si desde el principio?

El agradecimiento será tuyo por todos lados.

Conclusión: nunca hay suficiente si.

Ahí tienes. A.


Una buena cita de Code Complete:

Código como si quien mantiene su programa es un psicópata violento que sabe dónde vive.
- Anónimo

IOW, mantenlo simple. Si la legibilidad de su aplicación se mejorará mediante el uso de un predicado en un área particular, úselo. De lo contrario, use el ''si'' y continúe.


if no es malo (también sostengo que asignar moralidad a las prácticas de escritura de códigos es estúpido ...).

El Sr. Haines está siendo tonto y debería reírse de él.