J'ai récemment refactorisé du code au travail et je pensais avoir fait du bon travail. J'ai abandonné 980 lignes de code à 450 et divisé par deux le nombre de classes.
En montrant cela à mes collègues, certains n'étaient pas d'accord pour dire qu'il s'agissait d'une amélioration.
Ils ont dit - "moins de lignes de code n'est pas nécessairement meilleur"
Je peux voir qu'il peut y avoir des cas extrêmes où les gens écrivent de très longues lignes et / ou mettent tout en une seule méthode pour économiser quelques lignes, mais ce n'est pas ce que j'ai fait. À mon avis, le code est bien structuré et plus simple à comprendre / à maintenir car il est deux fois plus petit.
J'ai du mal à comprendre pourquoi quiconque voudrait travailler avec le double du code requis pour accomplir un travail, et je me demande si quelqu'un ressent la même chose que mes collègues et peut défendre avec succès le fait d'avoir plus de code par rapport à moins ?
la source
Réponses:
Une personne maigre n'est pas nécessairement en meilleure santé qu'une personne en surpoids.
Une histoire d’enfants de 980 lignes est plus facile à lire qu’une thèse de physique de 450 lignes.
De nombreux attributs déterminent la qualité de votre code. Certaines sont simplement calculées, comme la complexité cyclomatique et la complexité de Halstead . D'autres sont définies de manière plus vague, telles que la cohésion , la lisibilité, la compréhensibilité, l'extensibilité, la robustesse, la correction, l'autodocumentation, la propreté, la testabilité et bien d'autres.
Par exemple, si vous réduisez la longueur totale du code, vous pouvez introduire une complexité supplémentaire injustifiée et rendre le code plus crypté.
Diviser un long morceau de code en méthodes minuscules peut être aussi néfaste que bénéfique .
Demandez à vos collègues de vous expliquer précisément pourquoi, à leur avis, vos efforts de refactorisation ont produit un résultat indésirable.
la source
Fait intéressant, un de mes collègues et moi-même sommes actuellement au milieu d’un refactor qui augmentera le nombre de classes et de fonctions d’un peu moins du double, bien que les lignes de code restent à peu près identiques. J'ai donc un bon exemple.
Dans notre cas, nous avions une couche d'abstraction qui aurait vraiment dû être deux. Tout était entassé dans la couche ui. En le divisant en deux couches, tout devient plus cohérent, et le test et la maintenance des pièces individuelles deviennent beaucoup plus simples.
Ce n'est pas la taille du code qui gêne vos collègues, c'est autre chose. S'ils ne peuvent pas l'articuler, essayez de regarder le code vous-même comme si vous n'aviez jamais vu l'ancienne implémentation, et évaluez-le selon ses propres mérites plutôt que simplement en comparaison. Parfois, lorsque je fais un long refactor, je perds de vue l'objectif initial et je prends les choses trop loin. Examinez la situation dans son ensemble et remettez-le sur les rails, peut-être avec l'aide d'un programmeur en binôme dont vous appréciez les conseils.
la source
Une citation, souvent attribuée à Albert Einstein, me vient à l’esprit:
Lorsque vous réduisez les choses à la mer, cela peut rendre le code plus difficile à lire. Comme "facile / difficile à lire" peut être un terme très subjectif, j'expliquerai exactement ce que je veux dire par ceci: une mesure du degré de difficulté qu'un développeur expérimenté aura pour déterminer "qu'est-ce que ce code fait?" en regardant simplement la source, sans l'aide d'outils spécialisés.
Des langages comme Java et Pascal sont tristement célèbres pour leur verbosité. Les gens parlent souvent de certains éléments syntaxiques et disent avec dérision: "Ils ne sont là que pour faciliter le travail du compilateur". C'est plus ou moins vrai, sauf pour la partie "juste". Plus les informations sont explicites, plus le code est facile à lire et à comprendre, non seulement par un compilateur, mais également par un être humain.
Si je dis
var x = 2 + 2;
, il est immédiatement évident que celax
est supposé être un entier. Mais si je disvar foo = value.Response;
, il est beaucoup moins clair ce quifoo
représente ou quelles sont ses propriétés et capacités. Même si le compilateur peut facilement en déduire, cela met beaucoup plus d'effort cognitif sur une personne.Rappelez-vous que les programmes doivent être écrits pour que les gens puissent les lire et, accessoirement, pour que les machines puissent les exécuter. (Ironiquement, cette citation provient d’un manuel consacré à une langue réputée extrêmement difficile à lire!) C’est une bonne idée de supprimer les éléments redondants, mais ne supprimez pas le code qui facilite la tâche de vos semblables. comprendre ce qui se passe, même si ce n'est pas strictement nécessaire pour le programme en cours d'écriture.
la source
var
exemple n’est pas particulièrement bon en termes de simplification, car la plupart du temps, la lecture et la compréhension du code impliquent de déterminer le comportement à un certain niveau d’abstraction. Par conséquent, connaître les types réels de variables spécifiques ne change en général rien vous aide à comprendre les abstractions inférieures). Un meilleur exemple serait plusieurs lignes de code simple écrasées en une seule déclaration compliquée - par exemple, ilif ((x = Foo()) != (y = Bar()) && CheckResult(x, y))
faut du temps pour parler, et connaître les types dex
ou n'aider à rieny
.Un code plus long peut éventuellement être plus facile à lire. C'est généralement le contraire, mais il existe de nombreuses exceptions, dont certaines sont décrites dans d'autres réponses.
Mais regardons sous un angle différent. Nous supposons que le nouveau code sera considéré comme supérieur par la plupart des programmeurs expérimentés qui verront les deux éléments de code sans avoir une connaissance supplémentaire de la culture, de la base de code ou de la feuille de route de la société. Même dans ce cas, il existe de nombreuses raisons de s’opposer au nouveau code. Par souci de brièveté, je vais appeler «Les personnes critiquant le nouveau code». Pecritenc :
la source
Le type de code qui convient le mieux peut dépendre de l’expertise des programmeurs ainsi que des outils qu’ils utilisent. Par exemple, voici pourquoi ce qui serait normalement considéré comme du code mal écrit peut être plus efficace dans certaines situations que du code bien écrit orienté objet qui utilise pleinement l’héritage:
(1) Certains programmeurs n'ont tout simplement pas une compréhension intuitive de la programmation orientée objet. Si votre métaphore pour un projet logiciel est un circuit électrique, vous vous attendez à beaucoup de duplication de code. Vous aimerez voir plus ou moins les mêmes méthodes dans de nombreuses classes. Ils vous feront sentir à la maison. Et un projet dans lequel vous devez rechercher des méthodes dans les classes de parents ou même dans les classes de grands-parents pour voir ce qui se passe peut sembler hostile. Vous ne voulez pas comprendre le fonctionnement de la classe parente, ni comprendre en quoi la classe actuelle est différente. Vous voulez comprendre directement le fonctionnement de la classe actuelle et vous trouvez que le fait que les informations soient réparties sur plusieurs fichiers est source de confusion.
De même, lorsque vous souhaitez simplement résoudre un problème spécifique dans une classe spécifique, vous n'aurez peut-être pas envie de vous demander s'il faut résoudre le problème directement dans la classe de base ou écraser la méthode de votre classe d'intérêt actuelle. (Sans héritage, vous n'auriez pas à prendre une décision consciente. Par défaut, les problèmes similaires dans des classes similaires sont ignorés jusqu'à ce qu'ils soient signalés comme des bogues.) Ce dernier aspect n'est pas vraiment un argument valable, bien qu'il puisse expliquer certaines des opposition.
(2) Certains programmeurs utilisent beaucoup le débogueur. Même si en général je suis moi-même fermement du côté de l'héritage de code et de la prévention de la duplication, je partage une partie de la frustration que j'ai décrite dans (1) lors du débogage de code orienté objet. Lorsque vous suivez l'exécution de code, il saute parfois entre les classes (ancêtres) même s'il reste dans le même objet. En outre, lorsque vous définissez un point d'arrêt dans un code bien écrit, il est plus susceptible de se déclencher lorsqu'il ne vous est pas utile. Vous devrez donc peut-être vous efforcer de le rendre conditionnel (si possible), ou même de le poursuivre manuellement plusieurs fois avant le déclencheur approprié.
la source
Cela dépend totalement. Je travaille sur un projet qui n'autorise pas les variables booléennes en tant que paramètres de fonction, mais nécessite à la place une
enum
option dédiée pour chaque option.Alors,
est beaucoup plus verbeux que
cependant,
est beaucoup plus lisible que
Le compilateur devrait générer le même code pour les deux. Il n’ya donc rien à gagner en utilisant le formulaire le plus court.
la source
Je dirais que la cohésion pourrait être un problème.
Par exemple, dans une application Web, disons que vous avez une page d'administration dans laquelle vous indexez tous les produits, qui est essentiellement le même code (index) que celui que vous utiliseriez dans une page d'accueil, pour .. simplement indexer les produits.
Si vous décidez de tout partialiser pour rester au sec et élégant, vous devrez ajouter de nombreuses conditions si l'utilisateur navigant est administrateur ou non, et encombrer le code de choses inutiles qui le rendront très illisible, disons un designer!
Donc, dans une situation comme celle-ci, même si le code est à peu près le même, juste parce qu’il peut évoluer et que les cas d’utilisation peuvent légèrement changer, il serait mauvais de s’attaquer à chacun d’eux en ajoutant des conditions et des if. Une bonne stratégie consisterait donc à abandonner le concept DRY et à diviser le code en parties gérables.
la source
Quand moins de code est moins maintenable / extensible que plus de code. Le refactoring pour la concision du code supprime souvent les constructions de code "inutiles" dans l’intérêt de la LoC. Le problème, c’est que ces constructions de code, telles que les déclarations d’interface parallèle, les méthodes / sous-classes extraites, etc., sont nécessaires si ce code doit jamais faire plus que ce qu’il fait actuellement, ou le faire différemment. À la limite, certaines solutions personnalisées en fonction du problème spécifique risquent de ne pas fonctionner du tout si la définition du problème change légèrement.
Un exemple; vous avez une liste d'entiers. Chacun de ces entiers a une valeur en double dans la liste, sauf un. Votre algorithme doit trouver cette valeur non appariée. La solution générale consiste à comparer chaque nombre à tous les autres nombres jusqu'à ce que vous trouviez un nombre sans duplication dans la liste, ce qui correspond à une opération N ^ 2 fois. Vous pouvez également créer un histogramme à l'aide d'une table de hachage, mais c'est très inefficace. Cependant, vous pouvez le faire en temps linéaire et en espace constant en utilisant une opération XOR au niveau du bit; XOR chaque entier contre un "total" courant (commençant à zéro), et à la fin, la somme courante sera la valeur de votre entier non apparié. Très élégant. Jusqu'à ce que les exigences changent et que plusieurs numéros de la liste soient non appariés ou que les nombres entiers incluent zéro. Maintenant, votre programme retourne des résultats incompréhensibles ou ambigus (s'il renvoie zéro, cela signifie-t-il que tous les éléments sont appariés ou que l'élément non apparié est égal à zéro?). Tel est le problème des implémentations "intelligentes" dans la programmation en situation réelle.
la source
Le code informatique doit faire un certain nombre de choses. Un code "minimaliste" qui ne fait pas ces choses n'est pas un bon code.
Par exemple, un programme informatique devrait couvrir tout ce qui est possible (ou au minimum, tous les cas probables). Si un morceau de code ne couvre que le "cas de base" et en ignore les autres, ce n'est pas un bon code, même s'il est bref.
Le code informatique devrait être "évolutif". Un code crypté peut ne fonctionner que pour une seule application spécialisée, alors qu'un programme plus long mais plus ouvert peut faciliter l'ajout de nouvelles applications.
Le code informatique devrait être clair. Comme un autre intervenant l'a démontré, il est possible pour un codeur à noyau dur de produire une fonction de type "algorithmique" sur une ligne qui effectue le travail. Mais le one-liner a dû être divisé en cinq "phrases" différentes avant que cela soit clair pour le programmeur moyen.
la source
Performances informatiques. Lors de l'optimisation parallèle ou parallèle de certaines parties de votre code, il peut s'avérer avantageux, par exemple, de ne pas créer de boucles de 1 à 400, mais de 1 à 50 et de placer 8 instances de code similaire dans chaque boucle. Je ne présume pas que ce soit le cas dans votre cas, mais c’est un exemple où plus de lignes sont meilleures (performances).
la source