J'ai récemment lu ceci question qui fonctionne, la flèche anti-motif.
J'ai quelque chose de similaire dans le code que j'essaie de refracteur, sauf que cela branche. Il semble un petit quelque chose comme ça:
if(fooSuccess==true&&barSuccess!=true){
if(mooSuccess==true){
.....
}else if (mooSuccess!=true){
.....
}
}else if(fooSuccess!=true&&barSuccess==true){
if(mooSuccess==true){
.....
}else if (mooSuccess!=true){
if(cowSuccess==true){
.....
}else if (cowSuccess!=true){
.....
}
}
}
......
En bref, ça ressemble à ceci
if
if
if
if
do something
endif
else if
if
if
do something
endif
endif
else if
if
if
do something
endif
endif
endif
Contour emprunté à partir de codage horreur article sur le sujet
Et le code passe à travers différentes permutations de true et de faux pour divers drapeaux. Ces drapeaux sont définis quelque part "UP" dans le code ailleurs, soit par entrée de l'utilisateur, soit par le résultat d'une méthode.
Comment puis-je faire ce genre de code plus lisible? Mon intention est que, finalement, j'aurai un objet de type bean contenant tous les choix que le programmeur précédent a tenté de capturer avec cet anti-motif de ramification. Par exemple, si dans le contour du code ci-dessus, nous n'avons vraiment que trois, j'ai un énumé dans ce haricot:
enum ProgramRouteEnum{
OPTION_ONE,OPTION_TWO,OPTION_THREE;
boolean choice;
void setChoice(boolean myChoice){
choice = myChoice;
}
boolean getChoice(){
return choice;
}
}
Est-ce un traitement acceptable? Ou y a-t-il un meilleur?
Le code peut être simplifié comme ceci:
boolean condA = ( fooSuccess && !barSuccess && mooSuccess )
boolean condB = ( fooSuccess && !barSuccess && !mooSuccess )
boolean condC = (!fooSuccess && barSuccess && mooSuccess )
boolean condD = (!fooSuccess && barSuccess && !mooSuccess && cowSuccess)
boolean condE = (!fooSuccess && barSuccess && !mooSuccess && !cowSuccess)
if (condA) {
....
return;
}
if (condB) {
....
return;
}
... and so son
if (condE) {
....
return;
}
Essentiellement:
conA..condF
.ser61852 a une assez bonne réponse résolvant le problème général de simplifier les conditionnels imbriqués. Cependant, j'étais intrigué avec votre solution d'état d'état proposé et voulu illustrer comment cela peut parfois être préférable à un ensemble de drapeaux binaires.
Tout dépend de la séquence d'obtention des valeurs des indicateurs et du nombre de combinaisons valides. Si vous avez n
drapeaux, vous avez 2n États. Pour 4 drapeaux, c'est 16 états et comme vous l'avez observé, seuls 3 d'entre eux peuvent avoir une signification utile. Dans des situations comme ça, l'utilisation d'une variable d'état peut plutôt simplifier les choses grandement.
Disons que vous avez un verrou qui ouvrira si 4 clés sont pressées dans le bon ordre. Si vous appuyez sur une touche de manière incorrecte, elle se réinitialise immédiatement au début de la séquence. Un moyen très naïf pour mettre en œuvre un gestionnaire de frappes utilisant des drapeaux binaires est:
void key_pressed(char key)
{
if (!key1correct)
{
if (key == pin[0])
{
key1correct = true;
}
}
else if (!key2correct)
{
if (key == pin[1])
{
key2correct = true;
}
else
{
key1correct = false;
}
}
else if (!key3correct)
{
if (key == pin[2])
{
key3correct = true;
}
else
{
key1correct = false;
key2correct = false;
}
}
else
{
if (key == pin[3])
{
key4correct = true;
}
else
{
key1correct = false;
key2correct = false;
key3correct = false;
}
}
if (key1correct && key2correct && key3correct && key4correct)
{
open_lock();
key1correct = false;
key2correct = false;
key3correct = false;
key4correct = false;
}
}
Une version simplifiée et aplatie utilisant des drapeaux binaires (comme la réponse de User61852) ressemble à ceci:
void key_pressed(char key)
{
if (!key1correct && key == pin[0])
{
key1correct = true;
return;
}
if (key1correct && !key2correct && key == pin[1])
{
key2correct = true;
return;
}
if (key1correct && key2correct && !key3correct && key == pin[2])
{
key3correct = true;
return;
}
if (key1correct && key2correct && key3correct && key == pin[3])
{
open_lock();
key1correct = false;
key2correct = false;
key3correct = false;
return;
}
key1correct = false;
key2correct = false;
key3correct = false;
}
C'est beaucoup plus facile à lire, mais toujours dans ces deux solutions que vous avez des états comme key2correct && !key1correct
qui sont complètement invalides et inutilisés. Alors que l'utilisation d'une variable d'état num_correct_keys
au lieu des drapeaux binaires ressemblent à ceci:
void key_pressed(char key)
{
if (key == pin[num_correct_keys])
++num_correct_keys;
else
num_correct_keys = 0;
if (num_correct_keys == 4)
{
open_lock();
num_correct_keys = 0;
}
}
Certes, il s'agit d'un exemple artificiel, mais les gens écrivent souvent du code comme la première version dans des situations moins évidentes, répartis parfois sur plusieurs fichiers. L'utilisation d'une variable d'état simplifie souvent grandement le code, en particulier lorsque les indicateurs représentent une séquence d'événements.
Je ne suis pas sûr que votre exemple est particulièrement bon pour cette question, mais il est possible de le condenser pour être beaucoup plus simple et préserver toujours la même logique:
if(fooSuccess && !barSuccess)
{
if(mooSuccess)
{
// .....
}
else
{
// .....
}
}
else if (!fooSuccess && barSuccess)
{
if(mooSuccess)
{
// .....
}
else if(cowSuccess)
{
// .....
}
else
{
// .....
}
}
Notez comment il n'y a pas plus d'un niveau de if
s. Pour nettoyer cela, j'ai pris les étapes suivantes:
== true
, != true
, ou != false
. De temps en temps, j'ai trouvé ça vaut la peine de vérifier s'il est == false
Pour que cela apparaisse plus évident que c'est le cas attendu, mais même cela devrait être exceptionnel.else
ne consiste à rien d'autre qu'un bloc if
/else
bloque, vous pouvez la fusionner avec le parent else
.if
n'a qu'une entrée/variable, vous n'avez pas besoin de vérifier l'inverse pour le else
.