2010-09-08 5 views
18

FI Je suis actuellement en train de restructurer mon programme pour être plus OO et de mieux mettre en œuvre des modèles connus, etc.Comment éviter plusieurs imbriquées

J'ai assez nombreuses déclarations imbriquées-IF et veulent se débarrasser d'eux. Comment puis-je m'y prendre? Ma première approche consistait à le faire avec des exceptions, par exemple.

public static Boolean MyMethod(String param) { 
if (param == null) 
    throw new NullReferenceException("param may not be null"); 

if (param.Equals("none") || param.Equals("0") || param.Equals("zero")) 
    throw new ArgumentNullException("param may not be zero"); 

// Do some stuff with param 
// This is not executed if param is null, as the program stops a soon 
// as one of the above exceptions is thrown 
} 

La méthode est utilisée dans la classe principale de la demande, par ex.

static void Main() { 
try { 
    Boolean test = MyClass.MyMethod(null); // Will throw an exception 
} catch (Exception ex) { 
    MessageBox.Show(ex.Message, "Error"); 
} 

Je pense que cela est tout à fait agréable, car il empêche les déclarations imbriquées et la quasi-totalité des actions méthodes sont bien disposées sur un seul niveau.

Comme IF-déclarations, la méthode devrait ressembler à ceci

public Boolean MyMethod(String param) { 
if (param != null) { 
    if (!param.Equals("none") && !param.Equals("0") && !param.Equals("zero")) { 
    // Do some stuff with param 
    } else { 
    MessageBox.Show("param may not be zero", "Error"); 
} else { 
    MessageBox.Show("param may not be null", "Error"); 
} 
} 

que je trouve très, très laid et difficile à maintenir.

Maintenant, la question est; est cette approche bon? Je sais, cela peut être subjectif, mais comment surmontez-vous IFs imbriqués (1 ou 2 niveaux ne sont pas si mauvais, mais cela devient pire après cela ...)

+3

Cela peut être juste un exemple de code, mais vous devriez vraiment éviter de lancer Exception. Utilisez ArgumentNullException etc. –

+1

Et peut-être plus important encore, vous ne devriez pas lancer et ensuite attraper des exceptions comme ça. –

+0

Il est un exemple de code, j'essaie toujours de mieux jeter des exceptions nommées pour le cas. Et la capture ne se fait évidemment pas dans la même méthode, elle est faite à un niveau beaucoup plus élevé de l'application, plus ou moins juste avant qu'ils ne montent en flèche chez l'utilisateur. –

Répondre

17

Cela dépend vraiment de leur but. Dans votre premier exemple, les instructions if servent à l'exécution d'un contrat, en s'assurant que l'entrée de la méthode répond à certaines exigences. Dans ces cas, mon propre code a tendance à ressembler à votre code. Dans le cas de l'utilisation des blocs if pour contrôler le flux d'une méthode (plutôt que de faire respecter un contrat), cela peut parfois être un peu plus difficile. Parfois, je vous rencontriez un code comme l'exemple suivant (très simplifié):

private void SomeMethod() 
{ 
    if (someCondition == true) 
    { 
     DoSomething(); 
     if (somethingElse == true) 
     { 
      DoSomethingMore(); 
     } 
    } 
    else 
    { 
     DoSomethingElse(); 
    } 
} 

Dans ce cas, il semble que si la méthode a plusieurs responsabilités, dans ce cas je choisirais probablement de le diviser en plusieurs méthodes:

private void SomeMethod() 
{ 
    if (someCondition == true) 
    { 
     DoItThisWay(); 
    } 
    else 
    { 
     DoSomethingElse(); 
    } 
} 

private void DoItThisWay() 
{ 
    DoSomething(); 
    if (somethingElse == true) 
    { 
     DoSomethingMore(); 
    } 
} 

Cela rend chaque méthode beaucoup plus simple avec moins de code imbriqué, et peut également augmenter la lisibilité, si les méthodes sont donnés de bons noms.

+0

Voir mon commentaire - l'accrochage n'est pas fait dans la même méthode, c'était juste pour ne pas avoir à écrire plusieurs méthodes, D –

4

Vous voulez bien enquêter C#4 code contracts.

Un motif souvent utilisé est DDD specification pattern pour extraire des instructions if, bien que dans votre cas ce ne soit probablement pas approprié.

+0

C'est une chose très chouette ces contrats. Je vais les regarder. –

+0

Modèle de spécification: pourquoi ne pas utiliser un paramètre de type Func ? –

0

C'est vraiment une question assez vaste à répondre car cela dépend vraiment de la fonctionnalité des instructions if.

Dans la mesure du possible, j'essaie de remplacer les instructions if imbriquées par un commutateur, mais ce n'est pas toujours possible. La vérification de la validité des paramètres est une bonne approche, mais essayez de l'attraper plus haut dans le code, c'est-à-dire d'avoir les instructions throw, mais pas le catch dans la classe qui les lance.

+0

Voir la question révisée. C'était labyrinthe, c'est traité différemment dans le vrai code. –

17

Votre problème est connu comme l'anti-modèle pointe de flèche.

Il existe des approches pragmatiques, telles que les déclarations de la Garde que vous avez présentées dans votre échantillon, à des modèles de conception entiers, en évitant si (et d'autre) tous ensemble ...

Beaucoup de ressources sur la façon de les résoudre :

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

http://www.lostechies.com/blogs/chrismissal/archive/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern.aspx

http://elegantcode.com/2009/08/14/observations-on-the-if-statement/

1

Peut-être AspectF pourrait vous aider dans ce cas:

public Boolean MyMethod(String param) { 
try { 
    AspectF.Define 
    .ErrorMsgIfNull(param, "must be not null") 
    .ErrorMsgIfEquals(new string[] {"None", "Zero", "0"}, "may not be zero") 
    //... 
    // use your own "AspectFlets" you wrote 
    //... 
    .Do(() => 
    { 
    // Do some stuff with param 
    // This is not executed if param is null, as the program stops a soon 
    // as one of the above exceptions is thrown 
    }); 
} 

Si vous avez plusieurs conditions à remplir (ou à éviter) qui ferait un bloc laid imbriqué si, de cette façon au code factoriser peut vous aider à faire les choses un peu plus descriptif.

Les méthodes du code ci-dessus ne sont qu'un exemple qui n'existe pas, mais qui peut être facilement implémenté.

Questions connexes