Une correction de bogue récente m'a obligé à passer en revue le code écrit par d'autres membres de l'équipe, où j'ai trouvé ceci (c'est C #):
return (decimal)CostIn > 0 && CostOut > 0 ? (((decimal)CostOut - (decimal)CostIn) / (decimal)CostOut) * 100 : 0;
Maintenant, admettant qu'il y ait une bonne raison pour tous ces lancers, cela semble toujours très difficile à suivre. Il y avait un bug mineur dans le calcul et j'ai dû le démêler pour résoudre le problème.
Je connais le style de codage de cette personne lors de la révision du code, et son approche est que raccourcir est presque toujours préférable. Et bien sûr, il y a une valeur à cela: nous avons tous vu des chaînes inutilement complexes de logique conditionnelle qui pourraient être mises de l'ordre avec quelques opérateurs bien placés. Mais il est clairement plus apte que moi à suivre des chaînes d'opérateurs regroupés dans une seule déclaration.
Bien entendu, c’est finalement une question de style. Mais est-ce que quelque chose a été écrit ou fait des recherches pour reconnaître le point où aspirer à la brièveté du code cesse d'être utile et devient un obstacle à la compréhension?
La raison des conversions est Entity Framework. La base de données doit les stocker en tant que types nullables. Décimal? n’est pas équivalent à Decimal en C # et doit être converti.
la source
CostOut
est égal àDouble.Epsilon
, et est donc supérieur à zéro. Mais(decimal)CostOut
est dans ce cas zéro, et nous avons une erreur de division par zéro. La première étape devrait consister à obtenir le code correct , ce qui, à mon avis, ne l’est pas. Corrigez-le, créez des cas de test, puis rendez-le élégant . Le code élégant et le code bref ont beaucoup en commun, mais parfois la brièveté n'est pas l'âme de l'élégance.Réponses:
Pour répondre à votre question sur la recherche existante
Oui, il y a eu des travaux dans ce domaine.
Pour bien comprendre ces éléments, vous devez trouver un moyen de calculer une métrique afin de pouvoir effectuer des comparaisons quantitatives (plutôt que de procéder à une comparaison basée sur l’esprit et l’intuition, comme le font les autres réponses). Une mesure potentielle qui a été examinée est
Complexité cyclomatique ÷ Lignes de code source ( SLOC )
Dans votre exemple de code, ce rapport est très élevé, car tout a été compressé sur une seule ligne.
Lien
Voici quelques références si vous êtes intéressé:
McCabe, T. et A. Watson (1994), Complexité logicielle (CrossTalk: le journal du génie logiciel de la défense).
Watson, AH et McCabe, TJ (1996). Tests structurés: méthodologie de test utilisant la métrique de complexité cyclomatique (publication spéciale NIST 500-235). Extrait le 14 mai 2011 du site Web de McCabe Software: http://www.mccabe.com/pdf/mccabe-nist235r.pdf.
Rosenberg, L., T. Hammer, J. Shaw (1998). Métriques et fiabilité des logiciels (Actes du symposium international IEEE sur l’ingénierie de la fiabilité des logiciels). Extrait le 14 mai 2011 du site Web de la Penn State University: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.104.4041&rep=rep1&type=pdf.
Mon avis et solution
Personnellement, je n'ai jamais apprécié la brièveté, seulement la lisibilité. Parfois, la brièveté améliore la lisibilité, parfois non. Ce qui est plus important, c’est que vous écrivez un code vraiment lisible (ROC) au lieu d’un code en écriture seule (WOC).
Juste pour le plaisir, voici comment je l'écrirais et demanderais aux membres de mon équipe de l'écrire:
Notez également que l’introduction des variables de travail a l’effet secondaire heureux de déclencher une arithmétique en virgule fixe au lieu d’une arithmétique entière,
decimal
éliminant ainsi la nécessité de tous ces conversions .la source
if ((costIn < 0) || (costOut < 0)) throw new Exception("costs must not be negative");
La brièveté est bonne quand elle réduit l'encombrement autour des objets qui importent, mais quand elle devient concise , contenant trop de données pertinentes trop étroitement pour être facilement suivies, les données pertinentes deviennent elles-mêmes encombrées et vous avez un problème.
Dans ce cas particulier, les moulages
decimal
sont répétés encore et encore; dans l’ensemble, il serait probablement préférable de le réécrire comme suit:Soudain, la ligne contenant la logique est beaucoup plus courte et s'adapte à une ligne horizontale, de sorte que vous pouvez tout voir sans avoir à faire défiler l'écran, et la signification est beaucoup plus évidente.
la source
((decOut - decIn ) / decOut) * 100
à une autre variable.CostOut > 0
), vous devez développer cette condition en uneif
instruction. Cela n’a rien de mal à cela, mais cela ajoute plus de verbosité que la simple introduction d’un local.Bien que je ne puisse citer aucune recherche particulière sur le sujet, je suggérerais que tous les moulages de votre code enfreignent le principe «Ne vous répétez pas». Ce que votre code semble essayer de faire est de convertir le type
costIn
et lecostOut
en typeDecimal
, puis d'effectuer des contrôles de cohérence sur les résultats de ces conversions et d'exécuter des opérations supplémentaires sur ces valeurs converties si les contrôles aboutissent. En fait, votre code effectue l'un des contrôles de cohérence sur une valeur non convertie, ce qui augmente la possibilité que costOut contienne une valeur supérieure à zéro, mais inférieure à la moitié de la taille de la plus petite valeur non nulleDecimal
pouvant être représentée. Le code serait beaucoup plus clair s'il définissait des variables de typeDecimal
pour contenir les valeurs converties, puis agissait sur celles-ci.Il semble curieux que le rapport entre les
Decimal
représentations decostIn
et etcostOut
les valeurs réelles decostIn
etcostOut
, vous intéresse davantage , à moins que le code n'utilise également les représentations décimales à d'autres fins. Si le code doit utiliser davantage ces représentations, ce serait un argument supplémentaire pour la création de variables destinées à contenir ces représentations, plutôt que d'avoir une séquence continue de transtypages dans le code.la source
Decimal
distribution dépend de l'ampleur de la valeur en question, il m'est difficile d'imaginer des règles commerciales qui imposeraient un comportement réel des acteurs .Decimal
type ne le fait pas. La valeur 1.0d / 3.0 aura plus de chiffres à la droite de la décimale que ce qui peut être maintenu en utilisant des nombres plus grands. Donc, ajouter et soustraire le même nombre plus grand entraînera une perte de précision. Les types à point fixe peuvent perdre en précision avec la multiplication ou la division fractionnelle, mais pas avec l'addition, la soustraction, la multiplication ou la division avec le reste (par exemple, 1,00 / 7 correspond à 0,14 reste 0,2; 1,00 div 0,15 correspond à 6 reste 0,10).Je regarde ce code et demande "comment un coût peut-il être égal à 0 (ou moins)?". Quel cas particulier cela indique-t-il? Le code devrait être
Je devine quant aux noms ici: changer
BothCostsAreValidProducts
etNO_PROFIT
comme approprié.la source
if (CostIn <= 0 || CostOut <= 0)
est tout à fait bien.La brièveté cesse d’être une vertu quand on oublie que c’est un moyen de parvenir à une fin plutôt qu’une vertu en soi. Nous aimons la brièveté parce que cela correspond à la simplicité, et nous aimons la simplicité, car un code simple est plus facile à comprendre et à modifier et contient moins de bogues. En fin de compte, nous voulons que le code atteigne ces objectifs:
Répondre aux exigences de l'entreprise avec le moins de travail possible
Éviter les insectes
Permettez-nous d’apporter à l’avenir des changements qui continuent à remplir les objectifs 1 et 2
Ce sont les objectifs. Tous les principes ou méthodes de conception (qu’il s’agisse de KISS, YAGNI, TDD, SOLID, preuves, systèmes de types, métaprogrammation dynamique, etc.) ne sont vertueux que dans la mesure où ils nous aident à atteindre ces objectifs.
La ligne en question semble avoir manqué de vue l'objectif final. Bien que ce soit court, ce n'est pas simple. En fait, il contient une redondance inutile en répétant plusieurs fois la même opération de transtypage. La répétition de code augmente la complexité et la probabilité de bogues. Mélanger le casting avec le calcul réel rend également le code difficile à suivre.
La ligne a trois préoccupations: gardes (boîtier spécial 0), type casting et calcul. Chaque problème est assez simple lorsqu'il est pris isolément, mais parce qu'il a été mélangé dans la même expression, il devient difficile à suivre.
Il n’est pas clair pourquoi
CostOut
la première fois, elle n’est pas utiliséeCostIn
. Il peut y avoir une bonne raison, mais l'intention n'est pas claire (du moins sans contexte), ce qui signifie qu'un responsable serait prudent de ne pas modifier ce code car il pourrait y avoir des hypothèses cachées. Et ceci est un anathème pour la maintenabilité.Puisque
CostIn
est jeté avant de comparer à 0, je suppose que c'est une valeur à virgule flottante. (Si c'était un int, il n'y aurait aucune raison de lancer). Mais siCostOut
est un float, le code pourrait masquer un bogue obscur de division par zéro, puisqu’une valeur en virgule flottante peut être petite mais non nulle, mais nulle lorsqu’elle est convertie en décimale (du moins, j’estime que cela serait possible.)Le problème n'est donc pas la brièveté ni l'absence de concision, le problème est une logique répétée et une confusion des préoccupations menant à un code difficile à maintenir.
L'introduction de variables pour conserver les valeurs exprimées augmenterait probablement la taille du code compté en nombre de touches, mais réduirait la complexité, dissocierait les préoccupations et améliorerait la clarté, ce qui nous rapprocherait de l'objectif d'un code plus facile à comprendre et à gérer.
la source
La brièveté n'est pas une vertu du tout. La lisibilité est la vertu.
La brièveté peut être un outil pour atteindre la vertu, ou, comme dans votre exemple, peut être un outil permettant de réaliser quelque chose de totalement opposé. De cette façon ou d'une autre, il n'a presque aucune valeur en soi. La règle selon laquelle le code doit être "aussi court que possible" peut également être remplacée par "aussi obscène que possible" - elles sont toutes également dénuées de sens et peuvent être dommageables si elles ne servent pas un objectif plus ambitieux.
De plus, le code que vous avez posté ne suit même pas la règle de brièveté. Si les constantes avaient été déclarées avec le suffixe M, la plupart des
(decimal)
moulages horribles pourraient être évités, car le compilateur encouragerait resterint
àdecimal
. Je crois que la personne que vous décrivez utilise simplement la brièveté comme excuse. Probablement pas délibérément, mais quand même.la source
Au cours de mes années d'expérience, j'en suis venu à croire que la brièveté ultime est celle du temps - le temps domine tout le reste. Cela inclut à la fois le temps d'exécution - le temps nécessaire à un programme pour effectuer un travail - et le temps nécessaire à la maintenance - le temps nécessaire pour ajouter des fonctionnalités ou corriger des bogues. (La manière dont vous équilibrez les deux dépend de la fréquence à laquelle le code en question est exécuté par rapport à l'amélioration. Rappelez-vous que l' optimisation prématurée est toujours la racine de tout mal .) La brièveté du code a pour but d'améliorer la brièveté des deux; Un code plus court est généralement plus rapide, plus facile à comprendre et donc à maintenir. Si ce n'est pas le cas, alors c'est un net négatif.
Dans le cas présenté ici, je pense que la brièveté du texte a été mal interprétée comme une brièveté du nombre de lignes, au détriment de la lisibilité, ce qui peut augmenter le temps de maintenance. (Cela peut également prendre plus de temps, en fonction de la façon dont le lancer est effectué, mais à moins que la ligne ci-dessus ne soit exécutée des millions de fois, ce n'est probablement pas un problème.) Dans ce cas, les conversions décimales répétitives nuisent à la lisibilité, car il est plus difficile de voyez quel est le calcul le plus important. J'aurais écrit comme suit:
(Edit: c'est le même code que l'autre réponse, alors voilà.)
Je suis un fan de l'opérateur ternaire
? :
, donc je laisserais ça dans.la source
? :
- je pense que l'exemple ci-dessus est suffisamment compact, esp. comparé à un si-alors-sinon.:
.if-else
lit comme anglais: ne peut pas rater ce que cela signifie.Comme presque toutes les réponses ci-dessus, la lisibilité devrait toujours être votre objectif principal. Cependant, je pense aussi que le formatage peut être un moyen plus efficace d’atteindre cet objectif en créant des variables et de nouvelles lignes.
Je suis tout à fait d'accord avec l'argument de la complexité cyclomatique dans la plupart des cas, mais votre fonction semble être assez petite et assez simple pour être mieux traitée avec un bon cas de test. Par curiosité, pourquoi la nécessité de convertir en décimal?
la source
decimal
, non?double
! =decimal
, il y a une grande différence.Pour moi, il semble qu'un gros problème de lisibilité réside dans l'absence totale de formatage.
Je l'écrirais comme ceci:
Selon qu'il s'agisse d'un type
CostIn
et d'CostOut
un type à virgule flottante ou d'un type intégral, certains transtypages peuvent également être inutiles. Contrairement àfloat
etdouble
, les valeurs intégrales sont implicitement promuesdecimal
.la source
Le code peut être écrit rapidement, mais le code ci-dessus devrait dans mon monde être écrit avec des noms de variables bien meilleurs.
Et si je lis correctement le code, il essaie de faire un calcul de grossmargin.
la source
Je suppose que CostIn * CostOut sont des entiers
Voici comment je l' écrirais
M (Money) est décimal
la source
Le code est écrit pour être compris par les gens; la brièveté dans ce cas n’achète pas beaucoup et alourdit la charge du mainteneur. Pour cette brièveté, vous devez absolument le développer en rendant le code plus auto-documenté (de meilleurs noms de variables) ou en ajoutant plus de commentaires expliquant pourquoi il fonctionne de cette manière.
Lorsque vous écrivez du code pour résoudre un problème aujourd'hui, ce code risque de poser problème demain lorsque les exigences changent. La maintenance doit toujours être prise en compte et l'amélioration de la compréhensibilité du code est essentielle.
la source
La brièveté n'est plus une vertu quand
la source
Si cela réussissait les tests unitaires de validation, alors tout irait bien, si une nouvelle spécification était ajoutée, si un nouveau test ou une mise en œuvre améliorée était requise, et il était nécessaire de "démêler" la légèreté du code, c'est-à-dire lorsque problème se poserait.
De toute évidence, un bogue dans le code indique qu’il ya un autre problème avec Q / A, qui est un oubli. Par conséquent, le fait qu’un bogue n’ait pas été détecté est une source de préoccupation.
Lorsqu'il s'agit d'exigences non fonctionnelles telles que la "lisibilité" du code, il doit être défini par le responsable du développement, géré par le développeur principal et respecté par les développeurs afin de garantir une implémentation correcte.
Essayez d’assurer une mise en œuvre normalisée du code (normes et conventions) afin d’assurer la "lisibilité" et la facilité de "maintenabilité". Mais si ces attributs de qualité ne sont pas définis et appliqués, vous obtiendrez un code comme dans l'exemple ci-dessus.
Si vous n'aimez pas voir ce type de code, essayez de faire en sorte que l'équipe soit d'accord sur les normes et les conventions de mise en œuvre, écrivez-le et faites des examens de code aléatoires ou des contrôles inopinés pour valider que la convention est respectée.
la source