J'ai un morceau de code où j'itère une carte jusqu'à ce qu'une certaine condition soit remplie et ensuite j'utilise cette condition pour faire plus de choses.
Exemple:
Map<BigInteger, List<String>> map = handler.getMap();
if(map != null && !map.isEmpty())
{
for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
{
fillUpList();
if(list.size() > limit)
{
limitFlag = true;
break;
}
}
}
else
{
logger.info("\n>>>>> \n\t 6.1 NO entries to iterate over (for given FC and target) \n");
}
if(!limitFlag) // Continue only if limitFlag is not set
{
// Do something
}
Je sens que mettre un drapeau et ensuite l'utiliser pour faire plus de choses est une odeur de code.
Ai-je raison? Comment pourrais-je supprimer cela?
design-patterns
object-oriented
object-oriented-design
clean-code
Siddharth Trikha
la source
la source
entry
n'est utilisé nulle part à l'intérieur de la boucle de fonction, et nous ne pouvons que deviner ce quilist
est. EstfillUpList
censé remplirlist
? Pourquoi ne l'obtient-il pas comme paramètre?Réponses:
Il n'y a rien de mal à utiliser une valeur booléenne pour son objectif: enregistrer une distinction binaire.
Si on me disait de refactoriser ce code, je mettrais probablement la boucle dans une méthode qui lui est propre afin que l'affectation +
break
se transforme en areturn
; alors vous n'avez même pas besoin d'une variable, vous pouvez simplement direla source
fillUpList()
y a du code (que OP décide de ne pas partager) qui utilise réellement la valeurentry
de l'itération; sans cette hypothèse, il semblerait que le corps de la boucle n'ait rien utilisé de l'itération de la boucle.Ce n'est pas nécessairement mauvais, et parfois c'est la meilleure solution. Mais définir des indicateurs comme celui-ci dans des blocs imbriqués peut rendre le code difficile à suivre.
Le problème est que vous avez des blocs pour délimiter les étendues, mais que vous avez ensuite des indicateurs qui communiquent entre les étendues, rompant l'isolement logique des blocs. Par exemple, le
limitFlag
sera faux si lemap
estnull
, donc le code "faire quelque chose" sera exécuté si lemap
estnull
. C'est peut-être ce que vous envisagez, mais ce pourrait être un bogue qui est facile à manquer, car les conditions de cet indicateur sont définies ailleurs, à l'intérieur d'une portée imbriquée. Si vous pouvez conserver les informations et la logique dans l'étendue la plus étroite possible, vous devriez essayer de le faire.la source
if(map==null || map.isEmpty()) { logger.info(); return;}
ci, cependant, ne fonctionnera que si le code que nous voyons est le corps complet d'une fonction, et la// Do something
partie n'est pas requise dans le cas où la carte est nul ou vide.Je déconseille de raisonner sur les «odeurs de code». C'est juste la façon la plus paresseuse possible de rationaliser vos propres préjugés. Au fil du temps, vous développerez de nombreux préjugés, et beaucoup d'entre eux seront raisonnables, mais beaucoup d'entre eux seront stupides.
Au lieu de cela, vous devriez avoir des raisons pratiques (c'est-à-dire non dogmatiques) de préférer une chose à une autre, et éviter de penser que vous devriez avoir la même réponse pour toutes les questions similaires.
Les "odeurs de code" sont pour quand vous ne pensez pas . Si vous pensez vraiment au code, faites-le bien!
Dans ce cas, la décision pourrait vraiment aller dans les deux sens en fonction du code environnant. Cela dépend vraiment de ce que vous pensez être la façon la plus claire de penser à ce que fait le code. (Le code "propre" est un code qui communique clairement ce qu'il fait aux autres développeurs et leur permet de vérifier facilement qu'il est correct)
Souvent, les gens écriront des méthodes structurées en phases, où le code déterminera d'abord ce qu'il doit savoir sur les données, puis agira en conséquence. Si la partie "déterminer" et la partie "agir sur elle" sont toutes les deux un peu compliquées, alors il peut être judicieux de le faire, et souvent "ce qu'il doit savoir" peut être transporté entre les phases dans les drapeaux booléens. Je préférerais vraiment que vous donniez un meilleur nom au drapeau. Quelque chose comme "largeEntryExists" rendrait le code beaucoup plus propre.
Si, en revanche, le code "// Do Something" est très simple, il peut être plus judicieux de le placer dans le
if
bloc au lieu de définir un indicateur. Cela rapproche l'effet de la cause, et le lecteur n'a pas à analyser le reste du code pour s'assurer que l'indicateur conserve la valeur que vous définiriez.la source
Oui, c'est une odeur de code (cue downvotes de tous ceux qui le font).
L'essentiel pour moi est l'utilisation de la
break
déclaration. Si vous ne l'utilisez pas, vous parcourrez plus d'éléments que nécessaire, mais son utilisation donne deux points de sortie possibles de la boucle.Ce n'est pas un problème majeur avec votre exemple, mais vous pouvez imaginer qu'à mesure que le conditionnel ou les conditions à l'intérieur de la boucle deviennent plus complexes ou que l'ordre de la liste initiale devient important, il est plus facile pour un bogue de se glisser dans le code.
Lorsque le code est aussi simple que votre exemple, il peut être réduit à une
while
boucle ou une carte équivalente, une construction de filtre.Lorsque le code est suffisamment complexe pour nécessiter des indicateurs et des ruptures, il sera sujet à des bugs.
Donc, comme pour toutes les odeurs de code: si vous voyez un drapeau, essayez de le remplacer par un
while
. Si vous ne le pouvez pas, ajoutez des tests unitaires supplémentaires.la source
SO as with all code smells: If you see a flag, try to replace it with a while
Pouvez-vous développer cela avec un exemple?for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
parfor (Iterator<Map.Entry<BigInteger, List<String>>> iterator = map.entrySet().iterator(); iterator.hasNext() && !limitFlag; Map.Entry<BigInteger, List<String>> entry = iterator.next())
. C'est un schéma assez rare pour que j'aie plus de mal à le comprendre qu'une pause relativement simple.Utilisez simplement un nom autre que limitFlag qui indique ce que vous vérifiez réellement. Et pourquoi enregistrez-vous quelque chose lorsque la carte est absente ou vide? limtFlag sera faux, tout ce dont vous vous souciez. La boucle est très bien si la carte est vide, donc pas besoin de vérifier cela.
la source
Définir une valeur booléenne pour transmettre des informations que vous aviez déjà est une mauvaise pratique à mon avis. S'il n'y a pas d'alternative facile, cela indique probablement un problème plus important comme une mauvaise encapsulation.
Vous devez déplacer la logique de la boucle for dans la méthode fillUpList pour la faire rompre si la limite est atteinte. Vérifiez ensuite la taille de la liste directement après.
Si cela casse votre code, pourquoi?
la source
Commençons par le cas général: l'utilisation d'un indicateur pour vérifier si un élément d'une collection remplit une certaine condition n'est pas rare. Mais le schéma que j'ai vu le plus souvent pour résoudre ce problème consiste à déplacer le chèque dans une méthode supplémentaire et à en revenir directement (comme Kilian Foth l'a décrit dans sa réponse ):
Depuis Java 8, il existe une manière plus concise d'utiliser
Stream.anyMatch(…)
:Dans votre cas, cela ressemblerait probablement à ceci (en supposant
list == entry.getValue()
dans votre question):Le problème dans votre exemple spécifique est l'appel supplémentaire à
fillUpList()
. La réponse dépend beaucoup de ce que cette méthode est censée faire.Note latérale: En l'état, l'appel à
fillUpList()
n'a pas beaucoup de sens, car il ne dépend pas de l'élément que vous êtes en train d'itérer. Je suppose que c'est une conséquence de la suppression de votre code réel pour l'adapter au format de la question. Mais cela donne exactement un exemple artificiel difficile à interpréter et donc difficile à raisonner. Par conséquent , il est si important de fournir un minimal, complet et Vérifiable exemple .Je suppose donc que le code réel transmet le courant
entry
à la méthode.Mais il y a plus de questions à poser:
BigInteger
clés? S'ils ne sont pas vides, pourquoi avez-vous besoin de remplir les listes? Lorsqu'il y a déjà des éléments dans la liste, n'est-ce pas une mise à jour ou un autre calcul dans ce cas?Ce ne sont que quelques questions qui me sont venues à l'esprit lorsque j'ai essayé de comprendre le fragment de code. Donc, à mon avis, c'est la véritable odeur de code : votre code ne communique pas clairement l'intention.
Cela peut signifier cela ("tout ou rien" et atteindre la limite indique une erreur):
Ou cela peut signifier cela ("mise à jour jusqu'au premier problème"):
Ou ceci ("mettre à jour toutes les listes mais conserver la liste d'origine si elle devient trop grande"):
Ou même les suivants (en utilisant
computeFoos(…)
le premier exemple mais sans exception):Ou cela pourrait signifier quelque chose de complètement différent… ;-)
la source