Alors que je travaillais récemment dans une grande entreprise, j'ai remarqué que les programmeurs y suivaient ce style de codage:
Supposons que j'ai une fonction qui renvoie 12 si l'entrée est A, 21 si l'entrée est B et 45 si l'entrée est C.
Je peux donc écrire la signature de la fonction comme suit:
int foo(String s){
if(s.equals("A")) return 12;
else if(s.equals("B")) return 21;
else if(s.equals("C")) return 45;
else throw new RuntimeException("Invalid input to function foo");
}
Mais lors de l'examen du code, on m'a demandé de modifier la fonction comme suit:
int foo(String s){
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("A", 12);
map.put("B", 21);
map.put("C", 45);
return map.get(s);
}
Je ne peux pas me convaincre pourquoi le deuxième code est meilleur que le premier. Le deuxième code prendrait certainement plus de temps à s'exécuter.
La seule raison d'utiliser le deuxième code peut être qu'il offre une meilleure lisibilité. Mais si la fonction est appelée plusieurs fois, la deuxième fonction ne ralentira-t-elle pas le temps d'exécution de l'utilitaire qui l'appelle?
Que penses-tu de cela?
la source
switch
semble plus appropriée queif-else
). Mais à un moment donné, cela devient problématique. Le principal avantage de l'utilisation d'une carte est que vous pouvez la charger à partir d'un fichier ou d'une table, etc. Si vous codez en dur l'entrée sur la carte, je ne vois pas beaucoup de valeur sur un commutateur.Réponses:
Le but est de déplacer la création de la table de hachage en dehors de la fonction et de le faire une fois (ou juste moins de fois qu'autrement).
Cependant, java a depuis java7 pu avoir des chaînes (finales) dans les commutateurs:
la source
Dans votre deuxième exemple, le
Map
doit être un membre statique privé pour éviter la surcharge d'initialisation redondante.Pour de grandes quantités de valeurs, la carte fonctionnera mieux. En utilisant une table de hachage, on peut rechercher la réponse en temps constant. La construction multiple-if doit comparer l'entrée à chacune des possibilités jusqu'à ce qu'elle trouve la bonne réponse.
En d'autres termes, la recherche de carte est O (1) tandis que les
if
s sont O (n) où n est le nombre d'entrées possibles.La création de carte est O (n), mais n'est effectuée qu'une seule fois s'il s'agit d'un état constant statique. Pour une recherche effectuée fréquemment, la carte surclassera les
if
instructions à long terme, au prix d'un peu plus de temps au démarrage du programme (ou de la classe chargée, selon la langue).Cela dit, la carte n'est pas toujours le bon outil pour ce travail. C'est bien quand il y a beaucoup de valeurs, ou les valeurs doivent être configurables via un fichier texte, une entrée utilisateur ou une base de données (auquel cas la carte agit comme un cache).
la source
Il y a deux vitesses dans le logiciel: le temps qu'il faut pour écrire / lire / déboguer le code; et le temps qu'il faut pour exécuter le code.
Si vous pouvez me convaincre (et vos réviseurs de code) que la fonction de hachage est en effet plus lente que le if / then / else (après refactorisation pour faire un hashmap statique) ET vous pouvez me convaincre / réviseurs que son appelé suffisamment de temps pour faire un réel différence, puis allez-y et remplacez le hashmap par le if / else.
Sinon, le code de hachage est éminemment lisible; et (probablement) sans bug; vous pouvez le déterminer rapidement simplement en le regardant. Vous ne pouvez pas vraiment dire la même chose au sujet du if / else sans vraiment l'étudier; la différence est encore plus exagérée lorsqu'il existe des centaines d'options.
la source
Je préfère grandement la réponse de style HashMap.
Il y a une métrique pour cela
Il existe une métrique de qualité de code appelée Complexité cyclomatique . Cette métrique compte essentiellement le nombre de chemins différents à travers le code ( comment calculer la complexité cyclomatique ).
Pour chaque chemin d'exécution possible, une méthode devient de plus en plus difficile à comprendre ET à tester pleinement l'exactitude.
Cela revient au fait que "contrôler les mots clés" comme: ifs, elses, whiles, etc ... utilise des tests booléens qui peuvent être erronés. L'utilisation répétée de "contrôle des mots clés" produit un code fragile.
Des avantages supplémentaires
De plus, «l'approche basée sur la carte» encourage les développeurs à considérer les paires entrée-sortie comme un ensemble de données qui peut être extrait, réutilisé, manipulé au moment de l'exécution, testé et vérifié. Par exemple, ci-dessous, j'ai réécrit "foo" afin que nous ne soyons pas définitivement enfermés dans "A-> 12, B-> 21, C-> 45":
rachet_freak mentionne ce type de refactor dans sa réponse, il plaide pour la vitesse et la réutilisation, je plaide pour la flexibilité d'exécution (bien que l'utilisation d'une collection immuable puisse avoir d'énormes avantages selon la situation)
la source
Les données sont meilleures que le code. Pas moins parce qu'il est trop tentant d'ajouter une autre branche au code, mais il est difficile de se tromper en ajoutant une ligne à une table. Cette question en est un petit exemple. Vous écrivez une table de recherche. Soit écrire une implémentation, complète avec la logique conditionnelle et la documentation, ou écrire la table puis la rechercher.
Une table de données est toujours une meilleure représentation de certaines données que le code, l'optimisation modulo passe. La difficulté d'expression de la table peut dépendre du langage - je ne connais pas Java, mais j'espère qu'elle peut implémenter une table de recherche plus simplement que l'exemple de l'OP.
Il s'agit d'une table de recherche en python. Si cela est considéré comme un conflit invitant, veuillez considérer que la question n'est pas balisée java, le refactoring est indépendant du langage et que la plupart des gens ne connaissent pas java.
L'idée de restructurer le code pour réduire le temps d'exécution a du mérite, mais je préfère de loin utiliser un compilateur qui hisse la configuration courante plutôt que de le faire moi-même.
la source