RE:If anidados
Publicado por
Claudio (1 intervención) el 18/02/2005 05:18:18
Vamos por partes:
Hay dos razones principales por las cuales NO QUERES tener muchos if anidados, la primera salta a la vista y es que el código queda muy complejo para ser leído, pero lo más importante es que ese código se torna inmantenible debido a su complejidad.
Imaginate que tenés este código:
if (validacion1()) {
if (validacion2()) {
.........
if (validacionN()) // ya por la columna 300
alFinHagoLoQueQuriaHacer();
} else
fallo validacion N
..........
} else
fallo validacion 2
}
fallo validacion 1
Si estás desarrollando un sistema, y ese sistema va creciendo en complejidad, seguramente deberás agragar o modificar las validaciones que debes hacer, por lo que es fácil imaginar que este tipo de código es muy propenso a introducir errores.
Soluciones para esta situación vas a tener varias, y dependiendo de cual sea exactamente tu situación, y el lenguaje de programación que estés utilizando vas a poder resolverlo de muy variadas formas, ya que en un lenguaje orientado a objetos (Smalltalk, C++, Java, etc) tenés herramientas (herencia, polimorfismo, etc) que no tenes en un lenguaje procedural (Pascal, C, Basic, etc).
Solución 1:
Una que puede parecer facil es que en lugar de utilizar un if, utilice un switch o case de acuerdo el lenguaje en el cual podría ordenar el código de la siguiente manera:
switch(validacion()){
case FALLO1: fallo validacion 1;
case FALLO2: fallo validacion 2;
.....
case FALLON: fallo validacion N;
case VALIDACIONOK: alFinHagoLoQueQueriaHacer();
}
Pero debe notarse que en realidad lo que estoy haciendo es mover parte de la complejidad a la funcion validacion(), que es la que deberá saber que es lo que deve devolverme, lo cual probablemente nos lleve a que su código se parecería al código del cual partimos.
Solucion 2:
Basandonos en el código de ejemplo que acabo de citar, una solución muy sencilla sería que cambies la lógica del código por algo más sencillo. Evidentemente lo que se está haciendo en este código es que se ejecute alFinHagoLoQueQuriaHacer(), sí y solo sí se pasaron todas las validaciones, entonces yo podría expresarlo por su negación, es decir, que si alguna de las validaciones NO se cumple, entonces no ejecuto alFinHagoLoQueQuriaHacer(), sino que ejecuto el método fallo validacion correspondiente, esta lógica queda de la siguiente manera:
if (!validacion1()) {
fallo validacion 1
}
else if (!validacion2()) {
fallo validacion 2
}
else if (!validacion3()) {
fallo validacion 3
}
.....
else if (!validacionN()) {
fallo validacion N
}
else {
alFinHagoLoQueQueriaHacer();
}
Este código puede verse a simple vista que es mucho más simple y mantenible que el código anterior, donde era muy fácil confundir donde terminaba un if y comenzaba el siguiente, o que else correspondía a que if. El agregado de una nueva regla de validación es mucho más simple, por lo que su mantenimiento es menos costoso de realizar.
Este código y alguna variante en el que no tenga cada if dentro del else del if anterior, y que cada vez que ejecuto algún fallo validacion corte el flujo del programa de la forma en que lo permita el lenguaje (return, exit, etc), tal vez sean las soluciones más felices dentro del paradigma procedural.
Solucion 3:
Si cambiamos de paradigma y nos movemos a la orientación a objetos, yo podría generar un clase que contenga la lógica de cada uno de los algoritmos de validación que mi sistema pueda llegar a utilizar y que todos ellos entiendan el mensaje Validar(), es decir que serán polimorficos, así como también podría crear una clase (o conjunto de clases) que sepa agrupar a las primeras de acuerdo a operadores lógicos o matemáticos, y que cuando yo le de el mensaje de ejecutar la validación, se encargue de recorrer su conjunto de validadores ejecutandolos uno a uno y relacionando sus resultados de acuerdo a los operadores con los cuales fueron relacionados cada uno, de modo de que pueda decirme cual fue el resultado final de la ejecución de todos los validadores, de modo que el código me quedaría algo así:
CompositeValidator composite = new CompositeValidator();
composite.add(new Validador1()).and(new Validador2()).or(new Validador3())........xor(new ValidadorN());
if(composite.evaluate()){
alFInHagoLoQueQueriaHacer();
}
Validador1, Validador2, ValidadorN, todos entienden el mensaje validar(), y además a cada una de esas clases yo podría darles la responsabilidad de saber que hacer en caso de que falle la validación, o también podría hacer que cuando yo creo un objeto Validador, le paso por parámetro un objeto cuya sola responsabilidad sea hacer algo que será ejecutado cuando el validador lo decida, ya sea porque la validación falló o no.
Esta última solucion obviamente es la más poderosa de todas, ya que me deja organizar mi validación dinámicamente de acuerdo a lo que está pasando en el sistema en ese momento si es necesario ya que utilizo mucho la delegación. Así como también me permite tener muchos puntos de intercepción en los cuales podría interesarme hacer algo (cuando la validación falla, cuando no falla, antes o después de ejecutarse, etc), y que podría ser configurado facilmente en el objeto que a mi me interese, además de muchas otras ventajas que son más puntuales a lo que pueda estár necesitando.
Para los que les interese investigar, esta última solucion hace uso de algo conocido como "Design Patterns", más especificamente los llamados Composite y Command (buscar en google).
Como verán, el no utilizar muchos ifs anidados no es solo una "cuestión estética", o algo que no se hace porque simplemente se corre la voz o está mal visto que se haga, sino que tiene justificaciones alguna de las cuales he tratado de exponer, y seguramente me estaré olvidando de algunas otras.
Espero que valga la pena haber leído este mensaje y que puedan sacar algo en limpio y se entienda algo de todo lo que digo, con eso me conformo.
Saludos
Claudio