Question

C'est quelque chose qui m'a dérangé dans toutes les langues que j'ai utilisées, j'ai une instruction if mais la partie conditionnelle a tellement de vérifications que je dois la diviser sur plusieurs lignes, utiliser une instruction if imbriquée ou simplement accepter que c'est moche et passer à autre chose. avec ma vie.

Avez-vous trouvé d'autres méthodes qui pourraient être utiles à moi et à toute autre personne confrontée au même problème ?

Exemple, le tout sur une seule ligne :

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

Exemple, multiligne :

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

Exemple imbriqué :

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
Était-ce utile?

La solution

Séparez la condition en plusieurs booléens, puis utilisez un booléen maître comme condition.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

Mieux encore:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

Assurez-vous de donner à vos variables un nom qui indique réellement une intention plutôt qu'une fonction.Cela aidera grandement le développeur à maintenir votre code...cela pourrait être vous!

Autres conseils

Je suis surpris que personne n'ait encore reçu celui-ci.Il existe un refactoring spécifiquement pour ce type de problème :

http://www.refactoring.com/catalog/decomposeConditional.html

Il y a deux problèmes à résoudre ici :lisibilité et compréhension

La solution de « lisibilité » est une question de style et, en tant que telle, est sujette à interprétation.Ma préférence est la suivante :

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

ou ca:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

Cela dit, ce type de vérification complexe peut être assez difficile à analyser mentalement lors de la numérisation du code (surtout si vous n'êtes pas l'auteur original).Pensez à créer une méthode d'assistance pour éliminer une partie de la complexité :

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

Désormais, lors de l'analyse visuelle de la méthode "SomeMethod", la complexité réelle de la logique du test est masquée, mais la signification sémantique est préservée pour que les humains puissent la comprendre à un niveau élevé.Si le développeur a vraiment besoin de comprendre les détails, la méthode AreAllConditionsMet peut être examinée.

Ceci est officiellement connu sous le nom de modèle de refactorisation "Decompose Conditional", je pense.Des outils comme Resharper ou Refactor Pro !peut rendre ce type de refactoring facile !

Dans tous les cas, la clé pour avoir un code lisible et compréhensible est d’utiliser des noms de variables réalistes.Bien que je comprenne qu'il s'agit d'un exemple artificiel, "var1", "var2", etc. pas noms de variables acceptables.Ils doivent avoir un nom qui reflète la nature sous-jacente des données qu'ils représentent.

Je les divise souvent en variables booléennes de composants :

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...

Premièrement, je supprimerais tous les == true pièces, cela le rendrait 50% plus court ;)

Quand j'ai un gros problème, j'en cherche les raisons.Parfois, je vois que je devrais utiliser le polymorphisme, parfois je dois ajouter un objet d'état.Fondamentalement, cela implique qu'une refactorisation est nécessaire (une odeur de code).

Parfois j'utilise Les lois de De-Morgan pour simplifier un peu les expressions booléennes.

Vérifier Modèles de mise en œuvre par Kent Beck.Je pense à un modèle particulier qui pourrait aider dans cette situation...on l'appelle "Gardes".Plutôt que d'avoir des tonnes de conditions, vous pouvez les diviser en une garde, ce qui indique clairement quelles sont les conditions défavorables d'une méthode.

Ainsi, par exemple, si vous avez une méthode qui fait quelque chose, mais qu'il existe certaines conditions dans lesquelles elle ne devrait pas faire quelque chose, plutôt que :

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

Vous pourriez le changer en :

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

C'est un peu plus verbeux, mais beaucoup plus lisible, surtout lorsque vous commencez à avoir des imbrications bizarres, le garde peut vous aider (en combinaison avec des méthodes d'extraction).

D'ailleurs, je recommande FORTEMENT ce livre.

J'ai vu beaucoup de personnes et d'éditeurs soit indenter chaque condition de votre instruction if avec un onglet, soit la faire correspondre avec la parenthèse ouverte :

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

Je mets généralement la parenthèse proche sur la même ligne que la dernière condition :

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

Mais je ne pense pas que ce soit aussi propre.

Les conseils de Steve McConell, de Code terminé:Utilisez un tableau multidimensionnel.Chaque variable sert d'index de la table et l'instruction if se transforme en une recherche de table.Par exemple, if (size == 3 && poids> 70) se traduit par la décision d'entrée du tableau [Taille] [Weight_Group

Essayez de regarder les foncteurs et les prédicats.Le projet Apache Commons dispose d'un grand ensemble d'objets pour vous permettre d'encapsuler une logique conditionnelle dans des objets.Un exemple de leur utilisation est disponible sur O'reilly ici.Extrait de l'exemple de code :

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

Maintenant, les détails de tous ces prédicats isHonorRoll et les fermetures utilisées pour les évaluer :

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};

Eh bien, tout d’abord, pourquoi pas :

si (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

De plus, il est très difficile de refactoriser des exemples de code abstraits.Si vous montriez un exemple spécifique, il serait plus facile d'identifier un meilleur modèle pour répondre au problème.

Ce n'est pas mieux, mais ce que j'ai fait dans le passé :(La méthode suivante évite de court-circuiter les tests booléens, tous les tests sont exécutés même si le premier est faux.Ce n'est pas un modèle recommandé, sauf si vous savez que vous devez toujours exécuter tout le code avant de revenir - Merci à ptomato d'avoir repéré mon erreur !)

booléen ok = cond1 ;
ok &= cond2;
ok &= cond3;
ok &= cond4;
ok &= cond5;
ok &= cond6;

Ce qui équivaut à : (pas pareil, voir note ci-dessus !)

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

J'ai recours à des valeurs booléennes distinctes :

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}

Comme d'autres l'ont mentionné, j'analyserais vos conditions pour voir s'il existe un moyen de les sous-traiter à d'autres méthodes pour augmenter la lisibilité.

Dans les langages réflexifs comme PHP, vous pouvez utiliser des variables-variables :

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }

J'aime les décomposer par niveau, je formaterais donc votre exemple comme ceci :

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

C'est pratique quand on a plus d'imbrications, comme ceci (évidemment les conditions réelles seraient plus intéressantes que "= true" pour tout) :

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){

Si vous programmez en Python, c'est un jeu d'enfant avec le module intégré all() fonction appliquée sur la liste de vos variables (je vais simplement utiliser des littéraux booléens ici) :

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

Existe-t-il une fonction correspondante dans votre langage (C# ?Java?).Si tel est le cas, c'est probablement l'approche la plus propre.

McDowell,

Vous avez raison de dire que lorsque vous utilisez l'opérateur unique « & », les deux côtés de l'expression sont évalués.Cependant, lorsque vous utilisez l'opérateur '&&' (au moins en C#), la première expression à renvoyer false est la dernière expression évaluée.Cela rend le fait de placer l'évaluation avant l'instruction FOR aussi efficace que toute autre façon de le faire.

@tweakt

Ce n'est pas mieux, mais ce que j'ai fait dans le passé :

booléen ok = cond1 ;ok &= cond2;ok &= cond3;ok &= cond4;ok &= cond5;ok &= cond6;

Ce qui équivaut à :

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

En fait, ces deux choses ne sont pas identiques dans la plupart des langues.La deuxième expression cessera généralement d'être évaluée dès que l'une des conditions est fausse, ce qui peut représenter une amélioration considérable des performances si l'évaluation des conditions est coûteuse.

Pour des raisons de lisibilité, je préfère personnellement la proposition de Mike Stone ci-dessus.Il est facile de commenter de manière verbeuse et préserve tous les avantages informatiques d'une possibilité de retrait anticipé.Vous pouvez également appliquer la même technique en ligne dans une fonction si cela risque de perturber l'organisation de votre code en éloignant l'évaluation conditionnelle de votre autre fonction.C'est un peu ringard, mais vous pouvez toujours faire quelque chose comme :

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

le while (faux) est un peu ringard.J'aimerais que les langues aient un opérateur de portée appelé "une fois" ou quelque chose dont vous pourriez vous échapper facilement.

Si je le faisais en Perl, voici comment je pourrais exécuter les vérifications.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

Si vous envisagez de l'utiliser sur un sous-programme, remplacez chaque instance de last avec return;

J'aime diviser chaque condition en variables descriptives.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

par opposition à

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

et

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

par opposition à

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

La plupart des outils d'analyse statique pour examiner le code se plaindront si plusieurs expressions conditionnelles n'utilisent pas de parenthèses explicites dictant l'analyse des expressions, au lieu de s'appuyer sur des règles de priorité des opérateurs et moins de parenthèses.

L'alignement vertical au même niveau de retrait des accolades ouvrantes/fermantes {}, des parenthèses ouvrantes fermantes (), des expressions conditionnelles avec parenthèses et opérateurs à gauche est une pratique très utile, qui AMÉLIORE considérablement la lisibilité et la clarté du code plutôt que de tout brouiller. qui peut éventuellement être coincé sur une seule ligne, sans alignement vertical, ni espaces ni parenthèses

Les règles de priorité des opérateurs sont délicates, par ex.&& a une priorité plus élevée que ||, mais | a la priorité que &&

Donc, ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

est une expression conditionnelle multiple très facile à lire et à évaluer de manière incorrecte pour les simples humains.

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

Il n'y a rien de mal avec l'espace horizontal (sauts de ligne), l'alignement vertical ou l'évaluation explicite des expressions guidant les parenthèses, ce qui AMÉLIORE la lisibilité et la clarté.

Si tu fais ça:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

Ensuite, vous pouvez également répondre aux cas où quelque chose n'est pas vrai.Par exemple, si vous validez une entrée, vous pouvez donner à l'utilisateur un conseil sur la façon de la formater correctement, ou autre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top