Est-il jamais judicieux pour un refactoriste de se retrouver avec un LOC plus élevé? [fermé]

25

Y a-t-il des cas où un code plus verbeux (comme dans des déclarations plus logiques) est plus propre qu'un code plus concis?

nom d'utilisateur incorrect
la source
18
Bien sûr que oui. Même en éliminant la duplication, le code pour définir la nouvelle fonction extraite occupe son propre espace. L'écriture d'une nouvelle fonction peut prendre quatre lignes et n'en sauver que deux, tout en étant une bonne chose à faire.
Kilian Foth du
5
"code plus concis"? Je déteste vraiment la croyance erronée qu'un plus petit nombre de lignes signifie un "meilleur" code. Ce n'est pas le cas. En fait, c'est généralement le contraire. Il arrive un moment - et il est atteint très rapidement - où le fait d'encombrer de plus en plus de sens dans de moins en moins d'espace rend le code plus difficile à comprendre. En fait, il y a un concours entier - le Concours International de Code C Obfuscated - où de nombreux gagnants comptent sur ces limites de la compréhension humaine pour écrire du code impénétrable.
Andrew Henle
1
Le titre de votre question et votre question elle-même posent des questions différentes. Par exemple, une instruction if peut être changée en une expression ternaire qui est la même logiquement mais seulement 1 ligne.
Captain Man
1
-1 *** code plus verbeux (comme dans les déclarations plus logiques) *** la verbosité et le nombre de déclarations logiques sont deux choses indépendantes. C'est une mauvaise forme de changer les définitions.
Pieter B

Réponses:

70

Pour répondre à cela, prenons un exemple du monde réel qui m'est arrivé. En C # une bibliothèque que je maintiens, j'avais le code suivant:

TResult IConsFuncMatcher<T, TResult>.Result() =>
    TryCons(_enumerator) is var simpleMatchData && !simpleMatchData.head.HasValue
        ? _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
        : _recursiveConsTests.Any() 
            ? CalculateRecursiveResult() 
            : CalculateSimpleResult(simpleMatchData);

Discutant de cela avec des pairs, le verdict unanime était que les expressions ternaires imbriquées, couplées à l'utilisation "intelligente" du is varcode, étaient concises, mais difficiles à lire.

Je l'ai donc refactorisé pour:

TResult IConsFuncMatcher<T, TResult>.Result()
{
    var simpleMatchData = TryCons(_enumerator);

    if (!simpleMatchData.head.HasValue)
    {
        return _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
    }

    return _recursiveConsTests.Any() 
        ? CalculateRecursiveResult() 
        : CalculateSimpleResult(simpleMatchData);
}

La version originale ne contenait qu'une seule expression composée avec un implicite return. La nouvelle version contient désormais une déclaration de variable explicite, une ifdéclaration et deux explicites returns. Il contient plus d'instructions et plus de lignes de code. Pourtant, tous ceux que j'ai consultés ont jugé plus facile à lire et à raisonner, qui sont des aspects clés du «code propre».

La réponse à votre question est donc un «oui» catégorique, plus verbeux peut être plus propre qu'un code concis et constitue donc une refactorisation valide.

David Arno
la source
34
De nos jours, le cerveau des développeurs est une ressource plus rare que le disque, le CPU, la RAM ou la bande passante réseau. Ces autres choses sont importantes, et dans certaines applications, elles peuvent être votre facteur limitant, mais dans la plupart des cas, vous souhaitez optimiser la capacité de vos développeurs à comprendre le code en premier , puis ces autres choses.
anaximander
2
@anaximander, absolument d'accord. Écrivez du code pour que d'autres personnes lisent en premier et le compilateur en second. C'est pourquoi je trouve utile que d'autres personnes examinent mon code par des pairs, même si je suis le seul à le développer.
David Arno
4
Si je l'examinais, je suggérerais d'inverser l'ordre des déclarations de retour et de retirer le !de la condition. Je suggérerais également de mettre le deuxième retour dans un fichier else. Mais même en l'état, c'est une amélioration massive.
Martin Bonner soutient Monica
2
@DavidArno Je vois cette logique, et si if (!foo.HasValue)c'est un idiome dans votre code, encore plus fortement. Cependant, ce ifn'est pas vraiment une sortie anticipée - c'est un "faire ceci ou cela selon".
Martin Bonner soutient Monica
2
@fabric La comparaison des valeurs booléennes est dangereuse. Je l'évite autant que possible.
Martin Bonner soutient Monica
30

1. Manque de corrélation entre LOC et la qualité du code.

Le refactoring a pour objectif d'améliorer la qualité d'un morceau de code.

LOC est une métrique très basique qui, parfois, est en corrélation avec la qualité d'un morceau de code: par exemple, une méthode avec quelques milliers de LOC est susceptible d'avoir des problèmes de qualité. Il convient de noter, cependant, que LOC n'est pas la seule métrique et, dans de nombreux cas, n'a pas de corrélation avec la qualité. Par exemple, une méthode 4 LOC n'est pas nécessairement plus lisible ou plus maintenable qu'une méthode 6 LOC.

2. Certaines techniques de refactoring consistent à ajouter des LOC.

Si vous prenez une liste de techniques de refactoring , vous pouvez facilement repérer celles qui consistent à ajouter intentionnellement des LOC. Exemples:

Les deux sont des techniques de refactorisation très utiles, et leur effet sur le LOC est complètement hors de propos lorsque vous envisagez de les utiliser ou non.

Évitez d'utiliser LOC.

LOC est une métrique dangereuse. Il est très facile à mesurer et très difficile à interpréter correctement.

Jusqu'à ce que vous vous familiarisiez avec les techniques de mesure de la qualité du code, pensez à éviter de mesurer LOC en premier lieu. La plupart du temps, vous n'obtiendrez rien de pertinent, et il y aura des cas où cela vous induira en erreur en diminuant la qualité de votre code.

Arseni Mourzenko
la source
Vous avez refactorisé votre réponse et amélioré la qualité en ajoutant plus de LOT (lignes de texte): p
grinch
12

Si vous voulez voir le résultat final de simplement minimiser le nombre d'octets ou le nombre LoC de votre code source, allez jeter un œil aux soumissions sur le site Golf de Stack Exchange Code .

Si votre code source est réduit de cette façon, vous aurez bientôt un gâchis impossible à maintenir. Même si vous êtes la personne qui a écrit un tel code et que vous le comprenez parfaitement à l'époque, quelle sera votre efficacité lorsque vous y reviendrez dans six mois? Il n'y a aucune preuve qu'un tel code minimal s'exécute en fait plus rapidement non plus.

Le code doit être rédigé de manière à ce que tout membre de votre équipe puisse le consulter et comprendre immédiatement ce qu'il fait.

Peregrine
la source
Peut-être redondant, mais juste pour le préciser; si vous refactorisez le code pour plus de lisibilité, vous vous retrouvez toujours avec plus de LoC
JollyJoker
1

Oui, le refactoring peut définitivement générer plus de lignes de code.

Le cas le plus courant IMO est lorsque vous prenez du code qui n'est pas générique et que vous le rendez plus générique / flexible . La génération de code fait facilement augmenter considérablement les lignes de code (parfois d'un facteur deux ou plus).

Si vous vous attendez à ce que le nouveau code générique soit utilisé par d'autres (au lieu d'être simplement un composant logiciel interne) en tant que bibliothèque, vous finissez généralement par ajouter du code le plus unitaire et un balisage de documentation dans le code, ce qui augmentera à nouveau les lignes de code.

Par exemple, voici un scénario très courant qui se produit pour tous les développeurs de logiciels:

  • votre produit a besoin d'une nouvelle fonctionnalité urgente hautement prioritaire ou d'un correctif ou d'une amélioration de bogue dans deux semaines (ou quel que soit le délai considéré comme urgent pour la taille de votre projet / la taille de l'entreprise / etc.)
  • vous travaillez dur et livrez XYZ à temps et cela fonctionne. Toutes nos félicitations! Bon travail!
  • Pendant que vous développiez XYZ, votre conception / implémentation de code existante ne supportait pas vraiment XYZ mais vous pouviez shim XYZ dans la base de code
  • le problème est que la cale est moche et a une odeur de code terrible parce que vous avez fait des choses délicates / intelligentes / laides / mauvaises pratiques mais qui fonctionnent un peu
  • lorsque vous trouvez du temps plus tard, vous refactorisez le code, ce qui peut changer de nombreuses classes ou ajouter une nouvelle couche de classes et votre nouvelle solution est "bien faite" et n'a plus la mauvaise odeur de code ... "bonne façon" prend désormais beaucoup plus de lignes de code.

Quelques exemples concrets qui me viennent du haut de ma tête:

  • pour une interface de ligne de commande, vous pourriez avoir 5000 lignes de code if / else-if ou vous pourriez utiliser des rappels ... chaque rappel serait beaucoup plus petit et plus facile à lire / tester / vérifier / déboguer / etc mais si vous comptez des lignes de coder les 5 000 lignes laides du code if / else-if serait probablement plus petit
  • pour un morceau de code de traitement qui prend en charge N méthodes de traitement , vous pouvez à nouveau utiliser les instructions if / else qui auraient l'air les plus laides ...
    • ou vous pouvez passer aux rappels, ce qui serait mieux / mieux, mais les rappels prennent plus de lignes de code (compilez quand même le temps)
    • ou vous pouvez extraire davantage et faire des plugins qui peuvent être modifiés au moment de l'exécution. les plugins sont agréables car vous n'avez pas à recompiler le produit principal pour chaque nouveau plugin ou modification d'un plugin existant. et vous pouvez publier l'API pour que d'autres puissent étendre le produit. MAIS encore une approche plugin utilise plus de lignes de code.
  • pour une interface graphique, vous créez un super nouveau widget
    • vous ou un collègue remarque que le nouveau widget serait idéal pour XYZ et ABC, mais pour l'instant le widget n'est étroitement intégré que pour fonctionner pour XYZ
    • vous refactorisez le widget pour qu'il fonctionne pour les deux mais maintenant le nombre total de lignes de code augmente
Trevor Boyd Smith
la source