Estoy tratando de seguir las sugerencias de código limpio del tío Bob y específicamente para mantener los métodos cortos.
Sin embargo, no puedo acortar esta lógica:
if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}
No puedo eliminar los elses y así separar todo en pedazos más pequeños, porque el "else" en el "else if" ayuda al rendimiento: evaluar esas condiciones es costoso y si puedo evitar evaluar las siguientes condiciones, causa una de las primeras Es cierto, quiero evitarlos.
Incluso hablando semánticamente, evaluar la siguiente condición si se cumplió la anterior no tiene sentido desde el punto de vista comercial.
editar: esta pregunta se identificó como un posible duplicado de formas elegantes de manejar if (if else) else .
Creo que esta es una pregunta diferente (puedes ver eso también al comparar las respuestas de esas preguntas).
- Mi pregunta es verificar si la primera condición de aceptación finaliza rápidamente .
- La pregunta vinculada es tratar de tener todas las condiciones para aceptar y hacer algo. (mejor visto en esta respuesta a esa pregunta: https://softwareengineering.stackexchange.com/a/122625/96955 )
fuente
Respuestas:
Idealmente, creo que debería extraer su lógica para obtener el código / número de alerta en su propio método. Por lo tanto, su código existente se reduce hasta llegar a
y tiene GetConditionCode () encapsula la lógica para verificar las condiciones. Quizás también sea mejor usar una enumeración que un número mágico.
fuente
addAlert
necesite verificar la condición de alerta falsaAlertCode.None
.La medida importante es la complejidad del código, no el tamaño absoluto. Suponiendo que las diferentes condiciones son realmente solo llamadas de función única, al igual que las acciones no son más complejas de lo que ha mostrado, diría que el código no tiene nada de malo. Ya es tan simple como puede ser.
Cualquier intento de "simplificar" aún más complicará las cosas.
Por supuesto, puede reemplazar la
else
palabra clave con unreturn
como otros han sugerido, pero eso es solo una cuestión de estilo, no un cambio en la complejidad.Aparte:
Mi consejo general sería, nunca volverse religioso sobre ninguna regla para el código limpio: la mayoría de los consejos de codificación que ve en Internet son buenos si se aplican en un contexto adecuado, pero aplicar radicalmente ese mismo consejo en todas partes puede ganarle una entrada en El IOCCC . El truco siempre es lograr un equilibrio que permita a los seres humanos razonar fácilmente sobre su código.
Usa métodos demasiado grandes, y estás jodido. Usa funciones demasiado pequeñas y estás jodido. Evita las expresiones ternarias y estás jodido. Usa expresiones ternarias en todas partes, y estás jodido. Tenga en cuenta que hay lugares que requieren funciones de una línea y lugares que requieren funciones de 50 líneas (¡sí, existen!). Tenga en cuenta que hay lugares que requieren
if()
declaraciones, y que hay lugares que requieren el?:
operador. Use el arsenal completo que está a su disposición e intente usar siempre la herramienta más adecuada que pueda encontrar. Y recuerde, no se vuelva religioso incluso sobre este consejo también.fuente
else if
con un internoreturn
seguido de un simpleif
(eliminar elelse
) podría hacer que el código sea más difícil de leer . Cuando el código diceelse if
, inmediatamente sé que el código en el siguiente bloque solo se ejecutará si el anterior no lo hizo. Sin despeinarse sin problemas. Si es simple,if
entonces podría ejecutarse o no, independientemente de si el anterior se ejecutó. Ahora tendré que gastar un poco de esfuerzo mental para analizar el bloque anterior para notar que termina con areturn
. Prefiero gastar ese esfuerzo mental en analizar la lógica empresarial.else if
forma una unidad semántica. (No es necesariamente una sola unidad para el compilador, pero está bien)...; return; } if (...
. mucho menos si se extiende en varias líneas. Eso es algo que realmente tendré que mirar para ver qué está haciendo, en lugar de poder asimilarlo directamente con solo ver el par de palabras claveelse if
.else if
construcción, especialmente porque su forma encadenada es un patrón tan conocido. Sin embargo, el código del formularioif(...) return ...;
también es un patrón bien conocido, por lo que no lo condenaría por completo. Sin embargo, veo esto realmente como un problema menor: la lógica de flujo de control es la misma en ambos casos, y una simple mirada más cercana a unaif(...) { ...; return; }
escalera me dirá que de hecho es equivalente a unaelse if
escalera. Veo la estructura de un solo término, infiero su significado, me doy cuenta de que se repite en todas partes y sé qué pasa.else if
yreturn
. por ejemploelse if(...) { return alert();}
Es controvertido si esto es 'mejor' que el simple si ... para cualquier caso dado. Pero si quieres probar algo más, esta es una forma común de hacerlo.
Pon tus condiciones en objetos y pon esos objetos en una lista
Si se requieren múltiples acciones con la condición, puede hacer una recursión loca
Obviamente, sí. Esto solo funciona si tiene un patrón para su lógica. Si intenta realizar una acción condicional recursiva súper genérica, la configuración del objeto será tan complicada como la instrucción if original. Inventarás tu propio nuevo lenguaje / marco.
Pero su ejemplo hace tener un patrón
Un caso de uso común para este patrón sería la validación. En vez de :
Se convierte
fuente
if...else
escalera a la construcción de laConditions
lista. La ganancia neta es negativa, ya que la construcción deConditions
tomará tanto código como el código OP, pero la indirección adicional conlleva un costo de legibilidad. Definitivamente preferiría una escalera codificada limpia.conditions
se construye ... ARG! ¡No atributos de anotación! ¿Por que Dios? Ow mis ojos!Considere usar
return;
después de que una condición haya tenido éxito, le ahorra todos loselse
s. Incluso podría hacerloreturn addAlert(1)
directamente si ese método tiene un valor de retorno.fuente
if
s ... Eso podría ser una suposición razonable, y de nuevo podría no serlo.He visto construcciones como esta consideradas más limpias a veces:
Ternario con espacio correcto también puede ser una buena alternativa:
Supongo que también podría intentar crear una matriz con un par que contenga condición y función e iterar sobre ella hasta que se cumpla la primera condición, lo que, como veo, sería igual a la primera respuesta de Ewan.
fuente
case
etiquetas?Como una variante de la respuesta de @ Ewan, podría crear una cadena (en lugar de una "lista plana") de condiciones como esta:
De esta forma, puede aplicar sus condiciones en un orden definido y la infraestructura (se muestra la clase abstracta) omite las comprobaciones restantes después de que se haya cumplido el primero.
Aquí es donde es superior al enfoque de "lista plana", donde debe implementar el "salto" en el bucle que aplica las condiciones.
Simplemente configura la cadena de condición:
Y comience la evaluación con una simple llamada:
fuente
En primer lugar, el código original no es terrible IMO. Es bastante comprensible y no hay nada inherentemente malo en ello.
Luego, si no le gusta, desarrolle la idea de @ Ewan de usar una lista pero elimine su
foreach break
patrón poco natural :Ahora adapte esto en su idioma de elección, haga que cada elemento de la lista sea un objeto, una tupla, lo que sea, y estará bien.
EDITAR: parece que no está tan claro, pensé, así que déjame explicarte más.
conditions
es una lista ordenada de algún tipo;head
es el elemento actual que se está investigando: al principio es el primer elemento de la lista, y cada vez quenext()
se llama se convierte en el siguiente;check()
yalert()
son loscheckConditionX()
yaddAlert(X)
desde el OP.fuente
while not
lugar deforeach break
.La pregunta carece de algunos detalles. Si las condiciones son:
o si el contenido en
addAlert
es más complicado, entonces una solución posiblemente mejor en digamos c # sería:Las tuplas no son tan bellas en c # <8, sino que se eligen por conveniencia.
Las ventajas de este método, incluso si ninguna de las opciones anteriores se aplica, es que la estructura está estáticamente tipada. No puedes equivocarte accidentalmente por, digamos, perder un
else
.fuente
La mejor manera de reducir la complejidad ciclomática en los casos en que tiene mucho
if->then statements
es usar un diccionario o una lista (depende del idioma) para almacenar el valor clave (si es un valor de declaración o algún valor de) y luego un resultado de valor / función.Por ejemplo, en lugar de (C #):
Puedo simplemente
Si está utilizando lenguajes más modernos, puede almacenar más lógica que simplemente valores también (c #). Esto es realmente solo funciones en línea, pero también puede señalar otras funciones también si la lógica es poner en línea.
fuente
Su código ya es demasiado corto, pero la lógica en sí no debe cambiarse. A primera vista, parece que te estás repitiendo con cuatro llamadas
checkCondition()
, y solo es evidente que cada una es diferente después de volver a leer el código cuidadosamente. Debe agregar el formato y los nombres de función adecuados, por ejemplo:Su código debe ser legible por encima de todo. Después de leer varios de los libros del tío Bob, creo que ese es el mensaje que constantemente intenta transmitir.
fuente
Suponiendo que todas las funciones se implementen en el mismo componente, podría hacer que las funciones conserven algún estado para deshacerse de las múltiples ramas en el flujo.
EG:
checkCondition1()
se convertiríaevaluateCondition1()
, en el cual comprobaría si se cumplían las condiciones anteriores; si es así, almacena en caché algún valor para ser recuperadogetConditionNumber()
.checkCondition2()
se convertiríaevaluateCondition2()
, en el que comprobaría si se cumplían las condiciones anteriores. Si no se cumplió la condición anterior, verifica el escenario de condición 2, almacenando en caché un valor para ser recuperadogetConditionNumber()
. Y así.EDITAR:
Así es como debería implementarse la verificación de condiciones costosas para que este enfoque funcione.
Por lo tanto, si tiene demasiadas verificaciones costosas para realizar y las cosas en este código permanecen privadas, este enfoque ayuda a mantenerlo, permitiendo cambiar el orden de las verificaciones si es necesario.
Esta respuesta solo proporciona algunas sugerencias alternativas de las otras respuestas, y probablemente no será mejor que el código original si consideramos solo 4 líneas de código. Aunque, este no es un enfoque terrible (y tampoco hace que el mantenimiento sea más difícil como lo han dicho otros) dado el escenario que mencioné (demasiadas verificaciones, solo la función principal expuesta como pública, todas las funciones son detalles de implementación de la misma clase).
fuente
anyCondition() != false
.true
, el OP no quiere evaluar la condición 3.anyCondition() != false
dentro de las funcionesevaluateConditionXX()
. Esto es posible de implementar. Si no se desea utilizar el estado interno, entiendo, pero el argumento de que esto no funciona no es válido.Más de dos cláusulas "más" obligan al lector del código a recorrer toda la cadena para encontrar el que le interesa. Utilice un método como: void AlertUponCondition (condición condición) {switch (condición) {case Condition.Con1: ... break; Condición del caso. Con2: ... descanso; etc ...} Donde "Condición" es una enumeración adecuada. Si es necesario, devuelva un valor bool o. Llámalo así: AlertOnCondition (GetCondition ());
Realmente no puede ser más simple, Y es más rápido que la cadena if-else una vez que excede algunos casos.
fuente
No puedo hablar por su situación particular porque el código no es específico, pero ...
Un código como ese es a menudo un olor para un modelo OO carente. Realmente tiene cuatro tipos de cosas, cada una asociada con su propio tipo de alerta, pero en lugar de reconocer estas entidades y crear una instancia de clase para cada una, las trata como una cosa y trata de compensarlas más tarde, en un momento en que realmente necesita saber con qué está tratando para continuar.
El polimorfismo puede haber sido mejor para ti.
Sospeche del código con métodos largos que contienen construcciones largas o complejas de si-entonces. A menudo quieres un árbol de clases allí con algunos métodos virtuales.
fuente