J'essaie de suivre les suggestions de code propre d'Oncle Bob et en particulier de garder les méthodes courtes.
Je me trouve cependant incapable de raccourcir cette logique:
if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}
Je ne peux pas enlever les elses et donc séparer le tout en petits morceaux, provoquer le "else" dans le "else si" aide à la performance - évaluer ces conditions coûte cher et si je peux éviter d'évaluer les conditions ci-dessous, provoquer l'un des premiers est vrai, je veux les éviter.
Même sémantiquement parlant, évaluer la prochaine condition si la précédente était remplie n’a pas de sens du point de vue commercial.
edit: Cette question a été identifiée comme un doublon possible de façons élégantes de traiter si (sinon) autre chose .
Je pense que la question est différente (vous pouvez le constater également en comparant les réponses de ces questions).
- Ma question vérifie que la première condition d'acceptation se termine rapidement .
- La question liée est d'essayer d'avoir toutes les conditions à accepter pour faire quelque chose. (mieux vu dans cette réponse à cette question: https://softwareengineering.stackexchange.com/a/122625/96955 )
la source
Réponses:
Idéalement, je pense que vous devriez extraire votre logique pour obtenir le code / numéro d'alerte dans sa propre méthode. Donc, votre code existant est réduit jusqu’à
et GetConditionCode () encapsule la logique pour vérifier les conditions. Peut-être aussi mieux d'utiliser un Enum qu'un nombre magique.
la source
addAlert
à vérifier la condition d'alerte fictiveAlertCode.None
.La mesure importante est la complexité du code, pas la taille absolue. En supposant que les différentes conditions ne sont en réalité que des appels à une seule fonction, tout comme les actions ne sont pas plus complexes que ce que vous avez montré, je dirais que le code ne présente aucun défaut. C'est déjà aussi simple que possible.
Toute tentative de simplification supplémentaire compliquera les choses.
Bien sûr, vous pouvez remplacer le
else
mot - clé par un motreturn
comme d'autres l'ont suggéré, mais il ne s'agit que d'une question de style, pas de changement de complexité.De côté:
Mon conseil général serait de ne jamais devenir religieux avec une règle de code propre: la plupart des conseils de codage que vous voyez sur Internet sont bons s'ils sont appliqués dans un contexte approprié, mais l'application radicale de ces mêmes conseils partout peut vous faire gagner une entrée l' IOCCC . L'astuce consiste toujours à trouver un équilibre qui permette aux êtres humains de raisonner facilement sur votre code.
Utilisez trop de méthodes, et vous êtes foutus. Utilisez des fonctions trop petites et vous êtes foutus. Évitez les expressions ternaires, et vous êtes foutu. Utilisez des expressions ternaires partout et vous êtes foutus. Réalisez qu'il existe des endroits qui appellent des fonctions sur une ligne et des endroits qui appellent des fonctions à 50 lignes (oui, ils existent!). Sachez qu'il y a des endroits qui appellent des
if()
déclarations et des endroits qui appellent des?:
opérateurs. Utilisez tout l'arsenal à votre disposition et essayez de toujours utiliser l'outil le plus adapté que vous puissiez trouver. Et rappelez-vous, ne devenez pas religieux même à propos de ce conseil.la source
else if
par un caractère internereturn
suivi d'un simpleif
(suppression duelse
) pourrait rendre le code plus difficile à lire . Lorsque le code ditelse if
, je sais immédiatement que le code du bloc suivant ne sera exécuté que si le code précédent ne l’a pas été. Pas de muss, pas de chichi. S'il est une plaineif
alors il pourrait exécuter ou il ne pourrait pas, indépendamment du fait que la précédente exécution. Maintenant, je vais devoir faire un effort mental pour analyser le bloc précédent et constater qu'il se termine par unreturn
. Je préférerais consacrer cet effort mental à analyser la logique métier.else if
forme une unité sémantique. (Ce n'est pas nécessairement une seule unité au compilateur, mais c'est correct.)...; return; } if (...
N'a pas; encore moins si elle est répartie sur plusieurs lignes. C'est quelque chose que je devrai regarder pour voir ce qu'il fait, au lieu de pouvoir le prendre directement en regardant simplement la paire de mots-cléselse if
.else if
construction, d’autant plus que sa forme chaînée est un modèle bien connu. Cependant, le code de la formeif(...) return ...;
est également un modèle bien connu, je ne le condamnerais donc pas complètement. Je considère toutefois qu’il s’agit là d’un problème mineur: la logique du flux de contrôle est la même dans les deux cas, et un simple coup d’œil plus minutieux sur uneif(...) { ...; return; }
échelle me dira que c’est effectivement équivalent à uneelse if
échelle. Je vois la structure d'un seul terme, j'en déduis le sens, je me rends compte qu'il se répète partout et je sais ce qui se passe.else if
etreturn
. par exempleelse if(...) { return alert();}
Il est controversé de savoir si cela est «meilleur» que la plaine, si ..else pour un cas donné Mais si vous voulez essayer autre chose, c'est une façon courante de le faire.
Mettez vos conditions dans des objets et mettez ces objets dans une liste
Si plusieurs actions sont requises à condition que vous puissiez faire une récursion folle
Évidemment oui. Cela ne fonctionne que si vous avez un modèle dans votre logique. Si vous essayez d'effectuer une action conditionnelle récursive super générique, la configuration de l'objet sera aussi compliquée que l'instruction if d'origine. Vous allez inventer votre propre nouveau langage / framework.
Mais votre exemple ne un modèle
Un cas d'utilisation courant pour ce modèle serait la validation. Au lieu de :
Devient
la source
if...else
échelle dans la construction de laConditions
liste. Le gain net est négatif, car la construction deConditions
prendra autant de code que le code OP, mais le caractère indirectionnel ajouté a un coût en lisibilité. Je préférerais définitivement une échelle propre.conditions
est construit ... ARG! Pas d'annotation-attributs! Pourquoi Dieu? Ow mes yeux!Pensez à utiliser
return;
après une condition a réussi, il vous sauve tous leselse
s. Vous pourriez même pouvoirreturn addAlert(1)
directement si cette méthode a une valeur de retour.la source
if
s ... C’est peut-être une hypothèse raisonnable, mais ce n’est peut-être pas le cas.J'ai parfois vu des constructions comme celle-ci considérée comme plus propre:
Ternary avec un bon espacement peut également être une alternative intéressante:
J'imagine que vous pourriez également essayer de créer un tableau avec une paire contenant une condition et une fonction et l'itérer jusqu'à ce que la première condition soit remplie - ce qui, à mon avis, équivaudrait à la première réponse d'Ewan.
la source
case
étiquettes?Comme variante de la réponse de @ Ewan, vous pouvez créer une chaîne (au lieu d'une "liste simple") de conditions telles que:
De cette façon, vous pouvez appliquer vos conditions dans un ordre défini et l'infrastructure (la classe abstraite affichée) ignore les vérifications restantes une fois la première rencontrée.
C’est là où il est supérieur à l’approche "liste plate" dans laquelle vous devez implémenter le "saut" dans la boucle qui applique les conditions.
Vous configurez simplement la chaîne de conditions:
Et commencez l'évaluation avec un simple appel:
la source
Tout d'abord, le code d'origine n'est pas terrible OMI. C'est assez compréhensible et il n'y a rien de mauvais en soi.
Ensuite, si vous n’aimez pas cela, développez l’idée de @ Ewan d’utiliser une liste en supprimant son
foreach break
motif quelque peu anormal :Adaptez maintenant ceci dans la langue de votre choix, faites de chaque élément de la liste un objet, un tuple, peu importe, et vous serez doué.
EDIT: il semble que ce ne soit pas aussi clair que je le pensais, alors laissez-moi vous expliquer davantage.
conditions
est une liste ordonnée de quelque sorte;head
est l’élément en cours d’investigation - au début c’est le premier élément de la liste, et chaque foisnext()
qu’on l’appelle, il devient le suivant;check()
etalert()
sont lecheckConditionX()
etaddAlert(X)
de l'OP.la source
while not
au lieu deforeach break
.La question manque de quelques détails. Si les conditions sont:
ou si le contenu de
addAlert
est plus compliqué, alors une meilleure solution, par exemple, de c # serait:Les tuples ne sont pas si beaux en c # <8, mais choisis pour leur convenance.
Les avantages de cette méthode, même si aucune des options ci-dessus ne s’appliquent, est que la structure est typée de manière statique. Vous ne pouvez pas accidentellement bousiller par, par exemple, manquer un
else
.la source
Le meilleur moyen de réduire la complexité de Cyclomatic dans les cas où vous en avez beaucoup
if->then statements
est d'utiliser un dictionnaire ou une liste (dépendant de la langue) pour stocker la valeur de clé (si valeur d'instruction ou une valeur de), puis un résultat valeur / fonction.Par exemple, au lieu de (C #):
Je peux simplement
Si vous utilisez des langues plus modernes, vous pouvez stocker plus de logique que simplement des valeurs (c #). C’est vraiment juste des fonctions en ligne, mais vous pouvez aussi simplement indiquer d’autres fonctions si la logique est de les mettre en ligne.
la source
Votre code est déjà trop court, mais la logique elle-même ne devrait pas être modifiée. À première vue, il semble que vous vous répétiez avec quatre appels à
checkCondition()
, et il est évident que chacun est différent après avoir relu attentivement le code. Vous devez ajouter un formatage approprié et des noms de fonction, par exemple:Votre code doit être lisible avant tout. Ayant lu plusieurs livres de mon oncle Bob, je crois que c’est le message qu’il cherche constamment à faire passer.
la source
En supposant que toutes les fonctions soient implémentées dans le même composant, vous pouvez les conserver dans un état afin de vous débarrasser des multiples branches du flux.
EG:
checkCondition1()
deviendraitevaluateCondition1()
, sur lequel il vérifierait si la condition précédente était remplie; si tel est le cas, il met en cache une valeur à récupérergetConditionNumber()
.checkCondition2()
deviendraitevaluateCondition2()
, sur lequel il vérifierait si les conditions précédentes étaient remplies. Si la condition précédente n'était pas remplie, il vérifie le scénario de condition 2, mettant en cache une valeur à récupérergetConditionNumber()
. Etc.MODIFIER:
Voici comment la vérification des conditions coûteuses devrait être mise en œuvre pour que cette approche fonctionne.
Par conséquent, si vous avez trop de contrôles coûteux à effectuer et que les éléments de ce code restent confidentiels, cette approche permet de le maintenir, ce qui permet de modifier l'ordre des contrôles si nécessaire.
Cette réponse fournit simplement quelques suggestions alternatives des autres réponses, et ne sera probablement pas meilleure que le code d'origine si nous considérons seulement 4 lignes de code. Bien que ce ne soit pas une approche terrible (et ne rend pas la maintenance plus difficile, comme d’autres l'ont dit) compte tenu du scénario que j'ai mentionné (trop de contrôles, seule fonction principale exposée en tant que public, toutes les fonctions sont des détails de mise en œuvre de la même classe).
la source
anyCondition() != false
.true
, le PO ne veut pas que la condition 3 soit évaluée.anyCondition() != false
dans les fonctionsevaluateConditionXX()
. Ceci est possible à mettre en œuvre. Si l’approche consistant à utiliser l’état interne n’est pas souhaitée, je comprends, mais l’argument selon lequel cela ne fonctionne pas n’est pas valable.Pas plus de deux clauses "else" obligent le lecteur du code à parcourir l'intégralité de la chaîne pour trouver celle qui présente un intérêt. Utilisez une méthode telle que: void AlertUponCondition (Condition condition) {switch (condition) {case Condition.Con1: ... break; case Condition.Con2: ... pause; etc ...} Où "Condition" est une énumération appropriée. Si nécessaire, renvoyez une valeur booléenne ou une valeur. Appelez comme ça: AlertOnCondition (GetCondition ());
Cela ne peut vraiment pas être plus simple ET c'est plus rapide que la chaîne if-else une fois que vous avez dépassé quelques cas.
la source
Je ne peux pas parler de votre situation particulière car le code n'est pas spécifique, mais ...
Un code comme celui-ci est souvent une odeur de modèle OO manquant. Vous avez vraiment quatre types de choses, chacune associée à son propre type d’avertisseur, mais au lieu de reconnaître ces entités et de créer une instance de classe pour chacune d’elles, vous les traitez comme une seule chose et essayez de les rattraper plus tard, à un moment vraiment propice. besoin de savoir à quoi vous avez affaire pour pouvoir procéder.
Le polymorphisme pourrait vous convenir mieux.
Méfiez-vous du code avec les méthodes longues contenant des constructions if ou then longues ou complexes. Vous voulez souvent un arbre de classes avec des méthodes virtuelles.
la source