2009-10-22 14 views
13

¿Cuáles son las buenas maneras de manejar la lógica de negocios complicada que, a primera vista, requiere muchas sentencias if anidadas?¿Cómo simplificar la lógica complicada de negocios "IF"?

Ejemplo:

Cupón de descuento. podría ser:

1a) Descuento por valor
1b) Porcentaje de descuento

2a) de descuento normal
2b) de descuento progresivo

3a) Requiere acceso cupón
3b) no requieren acceso cupón

4a) Aplicado solo al cliente que ya compró antes
4b) Aplicado a cualquier cliente

5a) que se aplica a los clientes sólo de los países (X, Y, ...)

que requiere código aún más complicado de lo siguiente:

if (discount.isPercentage) { 
    if (discount.isNormal) { 
     if (discount.requiresAccessCoupon) { 
     } else { 
     } 
    } else if (discount.isProgressive) { 
     if (discount.requiresAccessCoupon) { 
     } else { 
     } 
    } 
} else if (discount.isValue) { 
    if (discount.isNormal) { 
     if (discount.requiresAccessCoupon) { 
     } else { 
     } 
    } else if (discount.isProgressive) { 
     if (discount.requiresAccessCoupon) { 
     } else { 
     } 
    } 
} else if (discount.isXXX) { 
    if (discount.isNormal) { 
    } else if (discount.isProgressive) { 
    } 
} 

Incluso si reemplaza FI para cambiar/caso se trata de todavía es demasiado complicado. ¿Cuáles son las formas de hacerlo legible, fácil de mantener, más comprobable y fácil de entender?

Respuesta

4

Escribo una máquina de estado genérica que se alimenta de listas de cosas para comparar.

9

Specification pattern podría ser lo que estás buscando.

Resumen:

En la programación de ordenadores, el patrón de especificación es un patrón de diseño de software en particular, con lo que la lógica de negocio puede recombinarse por el encadenamiento de la lógica de negocio juntos usando la lógica booleana.

2

La forma orientada a objetos de hacerlo es tener múltiples clases de descuento que implementan una interfaz común:

dicsount.apply(order) 

poner la lógica para determinar si la orden califica para el descuento en las clases de descuento.

+0

No son clases de descuento diferentes. Es una clase de descuento con todas esas cosas 1-5 como posibles propiedades. – Zelid

+0

Estoy sugiriendo que cambie eso para que TENGA múltiples clases de descuento. –

+0

Bien, ¿cómo se especifica que el descuento debería ser 1a, 2b, 3a, 4b al mismo tiempo? – Zelid

0

Mi primer pensamiento es que esto no es comprobable, lo que me lleva a una solución, para poder probarlo.

if (discount.isPercentage) { 
    callFunctionOne(...); 
} else if (discount.isValue) { 
    callFunctionThree(...); 
} else if (discount.isXXX) { 
    callFunctionTwo(...); 
} 

Luego puede hacer que cada declaración anidada sea una llamada separada. De esta manera puede probarlos individualmente y cuando prueba el grupo grande, sabe que cada uno funciona.

+0

Esto es como se implementó ahora, aún CallFunctionX() requiere muchos IF dentro de él, y así sucesivamente ... – Zelid

+0

Es posible que desee evaluar su lógica. Por ejemplo, si puede agrupar la funcionalidad en subclases, puede ver si este descuento se aplica a una subclase y pasarla allí. –

0

Realice métodos que comprueben un caso particular.

IsValueNormalAndRequiresCoopon bool (descuento Descuento) {...}

IsValueNormalAndRequiresCoupon bool (descuento Descuento) {...}

etc

Una vez que empiece a hacer que sea más fácil de ver dónde se puede abstraer la lógica común entre las opciones. A continuación, puede ir desde allí.

Para decisiones complejas a menudo termino con una clase que maneja los estados posibles.

+1

making ~ 32 funciones de este tipo para todas las combinaciones de propiedades no son tan buenas también – Zelid

+0

Cuando hayas terminado algunas de ellas, deberías comenzar a ver posibilidades para realizar comprobaciones comunes y volver a factorizar el código para una estructura más simple. La idea es eliminar algunos de los árboles para que pueda comenzar a ver el bosque. – ElGringoGrande

1

El uso de guard clauses podría ayudar.

+0

¿Pero esto no viola el principio de salida única? –

+0

Definitivamente. Aunque no sé cuánto significa esa regla más. Ciertamente no salgo de mi camino para seguirlo. Incluso si se suscribe, creo que las cláusulas de guardia serían una excepción aceptable. –

+1

cláusulas de guardia requerirán una nueva función por cada posible combinación de 5 diferentes propiedades de Cupón de descuento (Volumen/Porcentaje, Normal/Progresivo, RequireCoupon/DoNotRequireCoupon, ...) que tampoco es bueno – Zelid

11

Buena pregunta. La "Complejidad condicional" es un olor a código. Polymorphism es tu amigo.

La lógica condicional es inocente en su infancia, cuando es fácil de entender y está contenida dentro de unas pocas líneas de código . Desafortunadamente, rara vez envejece bien. Implementa varias características nuevas y . De repente, su lógica condicional se vuelve complicada y expansiva. [Joshua Kerevsky: Refactorizando a patrones]

Una de las cosas más simples que usted puede hacer para evitar anidada si los bloques es aprender a utilizar Guard Clauses.

double getPayAmount() { 
if (_isDead) return deadAmount(); 
if (_isSeparated) return separatedAmount(); 
if (_isRetired) return retiredAmount(); 
return normalPayAmount(); 
}; 

La otra cosa que he encontrado simplifica las cosas bastante bien, y lo que hace que su código de auto-documentado, es Consolidating conditionals.

double disabilityAmount() { 
    if (isNotEligableForDisability()) return 0; 
    // compute the disability amount 

Otros valiosos refactoring técnicas asociadas con expresiones condicionales incluyen Decompose Conditional, Replace Conditional with Visitor, y Reverse Conditional.

+0

+1 - Me gusta la forma en que diste diferentes opciones –

+1

Parece que la solución "Claves de guardia" solo funcionará si los atributos de comparación son excluyentes (_isDead, _isSeparated, isRetired) no es posible usar esta solución para el ejemplo proporcionado, porque Cupón de descuento podría ser "1a) Valor de descuento, 2a) Descuento normal, 3b) No requiere cupón de acceso, 4a) Aplicado solo al cliente que ya compró antes y 5a) Aplicado al cliente solo de países (X, Y, ...) " – Zelid

+0

o agregue cerca de 32 declaraciones de devolución de este tipo: _isValueAndNoramAndAcees() _isValueAndProgressiveAndNotRequireAccessAndRequireCountires() .... todas las demás combinaciones en IF yy en la función – Zelid

0

FWIW, he usado Hamcrest con gran éxito para este tipo de cosas. Creo que se podría decir que implementa el Patrón de Especificación, de lo que habló Arnis.

1

Usted debe ver realmente

Clean Code Talks - Inheritance, Polymorphism, & Testing
de Miško Hevery

Google Tech conversaciones 20 de noviembre de, 2008

RESUMEN

Es el código completo de sentencias if? Cambiar declaraciones? ¿Tiene la misma declaración de cambio en varios lugares? Cuando haces cambios, ¿te encuentras haciendo el mismo cambio en el mismo si/cambias en varios lugares? ¿Olvidaste alguna vez?

En esta charla se analizarán los enfoques para usar técnicas orientadas a objetos para eliminar muchos de esos condicionales. El resultado es un código más limpio, más ajustado y mejor diseñado que es más fácil de probar, comprender y mantener.

+0

Gracias, leerán los sonidos muy útiles – Zelid

Cuestiones relacionadas