2010-06-29 4 views
6

Je dois prendre un élément de données et lui appliquer un grand nombre de variables possibles. Je n'aime vraiment pas l'idée d'utiliser un ensemble gigantesque d'instructions if, donc je cherche de l'aide dans une approche pour simplifier, et le rendre plus facile à maintenir.Meilleure approche pour programmer des règles métier/mathématiques très complexes

À titre d'exemple:

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 .. avec éventuellement dans la gamme de 100-200 tests différents et des variations de la formule.

Cela ressemble juste à un cauchemar de maintenance. Aucune suggestion?

EDIT:

Pour ajouter encore au problème, de nombreuses variables ne sont utilisées que dans certaines situations, il est donc plus qu'un simple ensemble fixe de la logique avec des valeurs différentes. La logique elle-même doit changer en fonction des conditions, éventuellement des conditions appliquées à partir de variables précédentes (si val> seuil, par exemple). Donc, oui, je suis d'accord pour utiliser des recherches pour beaucoup de valeurs, mais je dois aussi avoir une logique variable.

+0

Possible dupe: http://stackoverflow.com/questions/1607252/how-to-simplify-complicated-business-if-logic –

Répondre

7

Une manière courante d'éviter les grandes structures de commutation est de placer l'information dans des structures de données. Créez une énumération SportType et une Dictionary<SportType, Int32> contenant les valeurs associées. Le vous pouvez simplement écrire val += sportTypeScoreMap[sportType] et vous avez terminé.

Des variantes de ce modèle vous aideront dans de nombreuses situations similaires.

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

Meilleure sémantique que la mienne. –

+0

Nice. Je vais probablement utiliser quelque chose comme ça. Cependant, j'ai élargi le problème un peu. Voir ma modification. –

+0

Vous atteindrez toujours un point où il n'est pas possible (ou utile) d'extraire plus de fonctionnalités dans les méthodes courantes - idéalement lorsque toutes les opérations que vous devez effectuer sont distinctes et qu'il ne reste aucun motif commun. Ce repos incompressible (l'analogie avec la compression de données convient très bien - on essaie d'extraire tous les motifs courants et tout ce qui reste ressemble à un bruit aléatoire sans structure) peut encore être très complexe.Le mieux que l'on puisse faire avec le reste est de le modulariser aussi bien que possible dans plusieurs petites méthodes avec de bons noms. –

0

En premier lieu, je serais probablement briser chaque zone de traitement logique dans sa propre méthode: (ne peut être les meilleurs noms comme une première passe)

EnforceSportRules 
ProcessSportDetails 
EnforceGenderRules 

Ensuite, en fonction de la complexité des règles sont , Je peux casser chaque section dans sa propre classe et les faire traiter par une classe principale (comme une usine).

GenderRules 
GenderContext 
0

J'ai rien de spécial à vous offrir que de recommander d'abord de ne pas laisser tout comme une grande block-- briser en sections, faire des séparateurs de commentaires entre les parties importantes. Une autre suggestion est si vous allez avoir beaucoup de tests très courts comme dans votre exemple, rompez avec la convention et placez les incrémenteurs val sur la même ligne que l'évaluation et le retrait afin qu'ils s'alignent les uns avec les autres.

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

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

blancs en excès peut faire ces centaines de choses en plusieurs centaines de lignes, ce qui pour obtenir une vue d'ensemble de la chose excessive défilement vertical et difficile.

1

Utilisez une instruction switch ou une fonction de filtre.

En fonction de filtre, je veux dire quelque chose comme:

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

Appliquer ensuite le filtre avec:

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

Notez que le filtre fonctionne bien mieux d'utiliser un dictionnaire, mais il n'est pas nécessaire.

+0

Vous me battez aussi. –

0

Si vous ajoutez simplement des valeurs dans ce type, je créerais une énumération avec des index définis correspondant aux valeurs stockées dans un tableau. Ensuite, vous pouvez faire quelque chose comme ceci:

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

creusage de The Pragmatic Programmer, vous pouvez utiliser une connexion DSL pour résumer les règles et écrire un moteur de processus. Pour votre problème présenté, une solution pourrait ressembler à:

MATCH{ 
    Soccer soccerBaseVal 

    IsMale 5 
    !IsMale 1 
} 

SWITCH{ 
    Monday 12 
    Tuesday 13 
} 

Puis correspondent tout dans le premier col de MATCH, et le premier élément de chaque commutateur que vous venez de. Vous pouvez faire n'importe quelle syntaxe, alors écrivez juste un peu de script pour l'entasser dans du code (ou utilisez Xtext parce que c'est plutôt cool).

1

Voici quelques idées:

1 Utilisez les tables de recherche:

var val = 0; 

SportType sportType = GetSportType(); 

val += sportvalues[sportType]; 

Vous pouvez charger la table de la base de données.

2 Utilisez le modèle d'usine:

var val = 0; 

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

Le Dynamic Factory Pattern est utile dans les situations étaient de nouveaux types (sport) sont ajoutés fréquemment au code. Ce modèle utilise la réflexion pour empêcher la modification de la classe de fabrique (ou de toute configuration globale). Cela vous permet d'ajouter simplement une nouvelle classe à votre code.

Bien sûr, l'utilisation d'une usine dynamique ou même d'une usine peut être exagérée dans votre situation. Tu es le seul à pouvoir le dire.

0

Envisagez d'implémenter le Strategy Pattern qui utilise l'héritage/le polymorphisme pour rendre la gestion des fonctions individuelles saine. En séparant chaque fonction dans sa propre classe dédiée, vous pouvez renoncer au cauchemar d'avoir des blocs de case miles ou de if déclarations.

Vous ne savez pas si C# le prend en charge (ou le sera toujours) mais VB.NET intègre les directives XML Comment CompletionList dans intellisense, ce qui - associé au Pattern Stratégie - peut vous donner la facilité d'utilisation d'un Enum avec l'extensibilité ouverte de OO.

Questions connexes