2010-06-29 8 views
6

Tengo que tomar un dato y aplicarle una gran cantidad de posibles variables. Realmente no me gusta la idea de usar un conjunto gigantesco de sentencias if, entonces estoy buscando ayuda en un enfoque para simplificar y hacer que sea más fácil de mantener.Mejor enfoque para programar reglas empresariales/matemáticas altamente complejas

Como un ejemplo:

if (isSoccer) 
    val = soccerBaseVal; 
else if (isFootball) 
    val = footballBaseVal; 
.... // 20 different sports 

if (isMale) 
    val += 1; 
else 
    val += 5; 

switch(dayOfWeek) 
{ 
    case DayOfWeek.Monday: 
     val += 12; 
    ... 
} 

etc .. etc .. etc .. con posiblemente en el rango de 100-200 pruebas diferentes y variaciones de fórmula.

Esto parece una pesadilla de mantenimiento. ¿Alguna sugerencia?

EDIT:

Para añadir aún más al problema, muchas variables sólo se utilizan en ciertas situaciones, por lo que es algo más que un conjunto fijo de la lógica con diferentes valores. La lógica misma tiene que cambiar en función de las condiciones, posiblemente las condiciones aplicadas a partir de variables anteriores (por ejemplo, val> threshold).

Así que sí, estoy de acuerdo con el uso de búsquedas para muchos de los valores, pero también tengo que tener una lógica variable.

+0

Posible engaño: http://stackoverflow.com/questions/1607252/how-to-simplify-complicated-business-if-logic –

Respuesta

7

Una forma común de evitar grandes estructuras de conmutación es poner la información en estructuras de datos. Cree una enumeración SportType y una Dictionary<SportType, Int32> que contenga los valores asociados. Simplemente puede escribir val += sportTypeScoreMap[sportType] y listo.

Las variaciones de este patrón lo ayudarán en muchas situaciones similares.

public enum SportType 
{ 
    Soccer, Football, ... 
} 

public sealed class Foo 
{ 
    private static readonly IDictionary<SportType, Int32> sportTypeScoreMap = 
     new Dictionary<SportType, Int32> 
     { 
      { Soccer, 30 }, 
      { Football, 20 }, 
      ... 
     } 

    private static readonly IDictionary<DayOfWeek, Int32> dayOfWeekScoreMap = 
     new Dictionary<DayOfWeek, Int32> 
     { 
      { DayOfWeek.Monday, 12 }, 
      { DayOfWeek.Tuesday, 20 }, 
      ... 
     } 

    public Int32 GetScore(SportType sportType, DayOfWeek dayOfWeek) 
    { 
     return Foo.sportTypeScoreMap[sportType] 
      + Foo.dayOfWeekScoreMap[dayOfWeek]; 
    } 
} 
+0

Mejor semántica que la mía. –

+0

Agradable. Probablemente usaré algo como esto. Sin embargo, he ampliado el problema un poco. Ver mi edición –

+0

Siempre llegará a un punto en el que no es posible (o útil) extraer más funcionalidad en métodos comunes; esto es ideal cuando todas las operaciones que debe realizar son distintas y no existe un patrón común. Este resto incompresible (la analogía con la compresión de datos se adapta bastante bien, uno trata de extraer todos los patrones comunes y todo lo que queda parece un ruido aleatorio sin estructura) puede ser muy complejo.Lo mejor que se puede hacer con el resto es modularlo lo mejor posible en varios métodos pequeños con buenos nombres. –

0

Como primer paso que probablemente romper cada área de procesamiento lógico en su propio método: (Puede no ser los mejores nombres como un primer pase)

EnforceSportRules 
ProcessSportDetails 
EnforceGenderRules 

A continuación, dependiendo de la complejidad de las reglas se , Puedo dividir cada sección en su propia clase y hacer que sean procesadas por una clase principal (como una fábrica).

GenderRules 
GenderContext 
0

tengo nada especial que ofrecer a recomendar primero en no dejarlo como un gran block-- dividirlo en secciones, hacer divisores de comentarios entre las partes importantes.

Otra sugerencia es si va a tener muchas pruebas muy cortas como en su ejemplo, salga de la convención y coloque los incrementos val en la misma línea que la evaluación y sangría para que se alineen entre sí.

if (isSoccer)    val = soccerBaseVal; 
if (isMale)    val += 1; 
else      val += 5; 

switch(dayOfWeek){ 
    case DayOfWeek.Monday: val += 12; 
    ... 
} 

espacios en blanco en exceso puede hacer que esos cien cosas en varios cientos de líneas, por lo que el desplazamiento vertical excesivo y difícil de obtener una visión general de la cosa.

1

Utilice una declaración de interruptor o una función de filtro.

Por función de filtro, quiero decir algo como:

func filter(var object, var value) 
{ 
    if(object == value) 
     object = valueDictionary['value']; 
} 

luego aplicar el filtro con:

filter(theObject, soccer) 
filter(theObject, football) 

Tenga en cuenta que el filtro funciona mucho mejor usar un diccionario, pero no es necesario.

+0

Me lo ganaste también. –

0

Si realmente solo está agregando valores de este tipo, crearía una enumeración con índices definidos que corresponden a los valores almacenados en una matriz. A continuación, puede hacer algo como esto:

enum Sport 
{ 
    football = 0, 
    soccer = 1, 
    //... 
} 

int sportValues[] = { 
    /* footballValue */, 
    /* soccerValue */, 
    /* ...Values */ 
}; 

int ApplyRules(Sport sport, /* other params */) 
{ 
    int value = startingValue; 
    value += sportValues[(int)sport]; 
    value += /* other rules in same fashion */; 
} 
1

de encofrado de El programador pragmático, podría utilizar una conexión DSL para encapsular las reglas y escribir un motor de procesos. Para su problema presentado, una solución podría ser:

MATCH{ 
    Soccer soccerBaseVal 

    IsMale 5 
    !IsMale 1 
} 

SWITCH{ 
    Monday 12 
    Tuesday 13 
} 

a continuación, coincide todo en la primera columna de marcadores, el primer elemento de cada interruptor se llega a. Puedes hacer la sintaxis que prefieras, luego solo escribir un poco de script para meterlo en el código (o usar Xtext porque se ve muy bien).

1

Aquí están algunas ideas:

1 Use las tablas de búsqueda:

var val = 0; 

SportType sportType = GetSportType(); 

val += sportvalues[sportType]; 

que es posible cargar la tabla de la base de datos.

2 Utilice el patrón de la fábrica:

var val = 0; 

val += SportFactory.Create(sportType).CalculateValue(); 

El Dynamic Factory Pattern es útil en situaciones eran nuevas (Sport) tipos frecuencia se añaden al código. Este patrón usa la reflexión para evitar que se cambie la clase de fábrica (o cualquier configuración global). Le permite simplemente agregar una nueva clase a su código.

Por supuesto, el uso de una fábrica dinámica, o incluso una fábrica puede ser excesiva en su caso. Eres el único que puede ver.

0

Considere implementar el Strategy Pattern que utiliza herencia/polimorfismo para hacer que la gestión de funciones individuales sea sensata. Al separar cada función en su propia clase dedicada, puede renunciar a la pesadilla de tener bloques case de millas de largo o declaraciones if.

No estoy seguro si C# lo admite aún (o lo hará) pero VB.NET integra las directivas XML Comment CompletionList en intellisense, que, cuando se combina con Strategy Pattern, puede darle la facilidad de uso de un Enum con la extensibilidad abierta de OO.

Cuestiones relacionadas