2017-10-12 7 views
0

J'ai un vieux code qui doit être ramené à la vie, il utilise environ 10-15 booléens qui dansent autour de la classe, comme ceci:Quelles sont les alternatives à l'utilisation de nombreux commutateurs booléennes

if (condition) 
{ 
    bool1 = true 
} 
if (condition) 
{ 
    bool2 = true 
} 
... 

puis

if (bool1 == true && bool2 == true && bool3 == false) 
{ 
    do something 
} 
else if (bool1 == true && bool2 == false && bool3 == false) 
{ 
    do something 
} 
... 

Est-il possible d'éviter ce type de codage? Existe-t-il de meilleures façons de mettre en œuvre cela? Peut-être en utilisant une carte? Je voudrais améliorer la lisibilité et les performances globales, puisque ce morceau de code a une longueur de plus de 1.000 lignes.

Après des commentaires ajouter par exemple plus précis:

boolean bool1 = false, bool2 = false, bool3 = false, bool4 = false, bool5 = false, 
bool6 = false, bool7 = false, bool8 = false, bool9 = false, bool10 = false; 

if (string_object.startsWith("Pattern1")) 
{ 
    bool1 = true 
} 
if (string_object.startsWith("Pattern2") 
{ 
    bool2 = true 
} 
if (string_object.startsWith("Pattern3") 
{ 
    bool3 = true 
} 
if (string_object.startsWith("Pattern4") 
{ 
    bool4 = true 
} 
if (string_object.startsWith("Pattern5") 
{ 
    bool5 = true 
} 
// and so on... 

if (system_type.equals("type1")) 
{ 
    if (bool1 == true && bool2 == true && bool3 == false) 
    { 
     system_value.set("value1") 
    } 
    else if (bool1 == true && bool2 == false && bool3 == false) 
    { 
     system_value.set("value2") 
    } 
    else if (bool1 == true && bool3 == false && bool4 == true) 
    { 
     system_value.set("value3") 
    } 
} 
else if (system_type.equals("type2")) 
{ 
    if (bool1 == true && bool2 == true && bool4 == true) 
    { 
     system_value.set("value1") 
    } 
    else if (bool1 == true && bool3 == false && bool5 == true) 
    { 
     system_value.set("value4") 
    } 
    else if (bool1 == true && bool3 == false && bool4 == true) 
    { 
     system_value.set("value5") 
    } 
} 
// and so on... 
+8

1. 'bool1 == true' ->' bool1' 2. la compression dépend de l'ensemble des conditions. –

+4

Cela dépend du but de tous les booléens. Rappelez-vous que l'écriture de code concerne autant la communication avec les futurs développeurs/responsables que le fait de dire à l'ordinateur ce qu'il doit faire. Si ces booléens ont des significations claires dans le contexte de ce que le programme doit faire, alors changez simplement leurs noms pour rendre ces significations claires. Si votre code est plus significatif après un refactoring, effectuez le refactoring. –

+2

Je suggère d'utiliser des méthodes avec de bons noms, donc vous voyez du nom ce que cette condition signifie réellement, pour évaluer vos "conditions" et les utiliser à la place ces 'bool1'' bool2' ... De cette façon vous pouvez incapsuler cette logique, évitez d'utiliser des variables globales et améliorez la lisibilité du code. –

Répondre

1

Vous pouvez construire des cartes de bits de votre booléens et encode souhaité combinaisons comme des entiers.

Voici un exemple: disons que vous avez besoin de trois booléens, flag0, flag1 et flag2, et vous devez vérifier cinq combinaisons différentes des drapeaux:

flag2 flag1 flag0 Action 
----- ----- ----- ---------- 
true false false ActionOne 
true true false ActionTwo 
true false true ActionThree 
false false true ActionFour 
false true true ActionFive 

Ensuite, vous pouvez construire des drapeaux comme suit:

int flags = 0; 
if (condition0) flags |= (1 << 0); 
if (condition1) flags |= (1 << 1); 
if (condition2) flags |= (1 << 2); 

maintenant, chaque combinaison de conditions est codé comme un numéro unique entre zéro et sept, y compris, afin qu'il puisse être vérifié par une déclaration switch:

switch(flags) { 
    case 4: actionOne(); break; // 1 0 0 
    case 6: actionTwo(); break; // 1 1 0 
    case 5: actionThree(); break; // 1 0 1 
    case 1: actionFour(); break; // 0 0 1 
    case 3: actionFive(); break; // 0 1 1 
} 
+0

Et encore mieux si vous utilisez un Enum pour les nommer – Cosine

+1

@Cosine Cela dépend beaucoup du projet, mais soit une énumération ou un commentaire détaillé est certainement dans l'ordre ici. – dasblinkenlight

+0

@Cosine, voulez-vous montrer en répondant? – k4s

0

Dans ce cas, je recommande de laisser les booléens seul, si elles sont bien étiquetés

Mais une chose propre peut se faire si elles sont étroitement liées (c.-à-direction, N/S/E/W), appelé bitmasks Stack connexes débordement après: what is a bitmask and a mask

Ils sont utiles pour la direction si vous avez une grille de rues, chaque intersection de la rue peut avoir N/S/E/routes W sortant de celui-ci, peut être défini comme 4 bits d'un nombre

Définissons quelques constantes

N=1 (0001) 
S=2 (0010) 
E=4 (0100) 
W=8 (1000) 

Une intersection avec des routes en N et E serait N | E

N|E=1|4=5 (0101) 

Une intersection complète + (NSEO) serait N | S | E | W

N|S|E|W=1|2|4|8=15 (1111) 

Si vous ajoutez à un masque de bits, newmask = oldmask | direction

Ajoutons S à notre masque NE

int newMask = oldMask | S 

oldmask était 0101, S est 0010, newmask devient 0111

Il a également un moyen facile de vérifier si une direction existe

Si nous voulons vérifier si oldmask contient N

boolean N? = (oldMask & N) != 0 

oldmask & N isolera le bit N, ce qui rend la valeur renvoyée être soit N ou 0

1

La plupart du temps, cet antipattern est dû aux développeurs qui ne veulent pas créer une sous-classe pour un seul nouveau comportement. Si tel est le cas, le polymorphisme pourrait aider.

Supposons que vous avez la classe suivante:

public class Animal { 
    private final boolean isCat; 
    private final boolean isReptile; 
    private final boolean isDog; 


    private Animal(final boolean isCat, final boolean isReptile, final boolean isDog) { 
    this.isCat = isCat; 
    this.isReptile = isReptile; 
    this.isDog = isDog; 
    } 

    public static Animal getLizard() { 
    return new Animal(false, true, true); 
    } 

    public static Animal getDog() { 
    return new Animal(false, false, false); 
    } 

    public String seeStranger() { 
    final StringBuilder result = new StringBuilder(this.toString()); 
    if (isDog) { 
     result.append(" barks and"); 
    } else if (isCat) { 
     result.append(" meows and"); 
    } 
    if (isReptile) { 
     result.append(" crawls away."); 
    } else { 
     result.append(" walks forward."); 
    } 
    return result.toString(); 
    } 
} 

Qu'est-ce que vous voulez vraiment sont plusieurs classes ayant des comportements différents:

public abstract class Animal { 


    public static Animal getLizard() { 
    return new Lizard(); 
    } 

    public static Animal getDog() { 
    return new Dog(); 
    } 

    public abstract String seeStranger(); 

    private static class Lizard extends Animal { 
    @Override 
    public String seeStranger() { 
     return this.toString() + " crawls away."; 
    } 
    } 

    private static class Dog extends Animal { 
    @Override 
    public String seeStranger() { 
     return this.toString() + " barks and walks forward."; 
    } 
    } 
} 
1

En fonction de votre cas d'utilisation, vous trouverez peut-être plus facile de se passer Les variables booléennes complètement et mettent juste les conditions en ligne dans les instructions de flux de contrôle. Par exemple, vous pouvez changer ceci:

if (condition1) 
{ 
    bool1 = true 
} 
else if (condition2) 
{ 
    bool2 = true 
} 

... 

if (bool1 == true && bool2 == true && bool3 == false) 
{ 
    do something 
} 
else if (bool1 == true && bool2 == false && bool3 == false) 
{ 
    do something 
} 

... 

pour être quelque chose comme ceci:

if (condition1 && condition2 && condition3) 
{ 
    do something 
} 
else if (condition1 && (not condition2) && codition3) 
{ 
    do something 
} 

... 

Vous pourriez également être en mesure de simplifier vos conditions. Par exemple, il peut y avoir un condition4 équivalent à condition1 && condition2 qui n'utilise pas &&. Considérons condition1 := x <= 100 et condition2 := x >= 100. condition1 && condition2 est totalement équivalent à x == 100, et je recommanderais certainement changer

... 
bool1 = (x >= 100); 
... 
bool2 = (x <= 100); 
... 
if (bool1 == true && bool2 == true) { ... } 
... 

à ce lieu:

... 
if (x == 100) { ... } 
... 

Je hésite à aller aussi loin que d'appeler l'utilisation explicite des variables booléennes un de AntiModèle, mais J'ai tendance à préférer garder les Booléens comme valeurs R chaque fois que c'est possible.

3

Il y a plusieurs choses que vous pouvez faire.

  1. comme d'autres l'ont mentionné, le code comme ceci:

    if (condition) { bool1 = true; }

peut être comprimé à:

bool1 = (condition); 
  1. Un autre outil utile est l'un des refactors de Martin Fowler - Decompose Conditional.

Un exemple de cela serait quelque chose comme changer ceci:

if (bool1 == true && bool2 == true && bool3 == false) 
{ 
    do something 
} 
else if (bool1 == true && bool2 == false && bool3 == false) 
{ 
    do something 
} 

à quelque chose comme ceci:

if (firstCondition()) 
{ 
    do something 
} 
else if (secondCondition()) 
{ 
    do something 
} 

private boolean firstCondition() { 
    return (bool1 && bool2 && !bool3); 
} 

private boolean secondCondition() { 
    return (bool1 && !bool2 && !bool3); 
} 

Decomposing conditionals complexes comme cela rend votre code plus facile à lire et à maintenir.