web-dev-qa-db-fra.com

Qu'est-ce que la refactorisation et qu'est-ce que la modification du code?

Je sais que le refactoring "change la structure d'un programme pour que la fonctionnalité ne soit pas modifiée". Je parlais avec certains des gars avec qui je travaille sur mon projet de dernière année à l'université et j'ai été surpris qu'ils aient une vue beaucoup plus expansive (à défaut d'un meilleur Word) de refactoring.

Je considère le refactoring comme des choses comme extraire des méthodes et renommer des classes. Ils ont également suggéré des choses comme changer les structures de données (comme un Java LinkedList en ArrayList), changer les algorithmes (en utilisant le tri par fusion au lieu du tri par bulles), et même la réécriture de gros morceaux de code comme refactoring.

J'étais tout à fait sûr qu'ils s'étaient trompés, mais je n'ai pas été en mesure de donner une bonne raison parce que ce qu'ils proposaient a changé le programme (et sans doute l'a amélioré) sans changer son comportement. Ai-je raison, et surtout, pourquoi?

73
David Johnstone

Martin Fowler's "Refactoring: Improving the Design of Existing Code" est peut-être LA référence:

Le refactoring est une technique contrôlée pour améliorer la conception d'une base de code existante. Son essence est d'appliquer une série de petites transformations préservant le comportement, dont chacune "trop ​​petite pour en valoir la peine". Cependant, l'effet cumulatif de chacune de ces transformations est assez important. En les faisant par petites étapes, vous réduisez le risque d'introduction d'erreurs. Vous évitez également d'avoir le système en panne pendant que vous effectuez la restructuration - ce qui vous permet de refaçonner progressivement un système sur une longue période de temps.

Le refactoring va de pair avec les tests unitaires. Écrivez les tests avant de refactoriser puis vous avez un niveau de confiance dans le refactoring (proportionnel à la couverture des tests).

Une bonne référence est: Informations sur la refactorisation

68
Mitch Wheat

Fowler trace une ligne claire entre les modifications du code qui affectent et celles qui ne le font pas, affectent son comportement. Il appelle ceux qui ne le font pas "refactoring". Ceci est une distinction importante, car si nous divisons notre travail en activités de modification de code de refactorisation et de non-refactorisation (Fowler l'appelle "porter des chapeaux différents"), nous pouvons appliquer différentes techniques adaptées à l'objectif.

Si nous effectuons un refactoring ou une modification de code préservant le comportement:

  • tous nos tests unitaires doivent réussir avant et après la modification
  • nous ne devrions pas avoir besoin de modifier des tests ou d'en écrire de nouveaux
  • nous attendons un code plus propre lorsque nous aurons terminé
  • nous n'attendons pas de nouveaux comportements

Si nous apportons une modification de code qui change le comportement:

  • nous attendons un nouveau comportement
  • nous devons écrire de nouveaux tests
  • nous pouvons obtenir du code plus sale lorsque nous avons terminé (et nous devrions alors le refactoriser)

Si nous perdons de vue cette distinction, alors nos attentes pour une tâche de modification de code donnée sont confuses et complexes, ou en tout cas plus confuses et plus complexes que si nous en sommes conscients. C'est pourquoi la Parole et sa signification sont importantes.

32
Carl Manaster

Pour donner mon avis:

Petits changements incrémentiels qui laissent le code dans un meilleur état qu'il n'a été trouvé

Certainement oui: Modifications "cosmétiques" qui ne sont pas directement liées aux fonctionnalités (c'est-à-dire qu'elles ne sont pas facturables comme demande de modification).

Certainement non: La réécriture de gros morceaux viole clairement la partie "petite, incrémentielle". La refactorisation est souvent utilisée comme opposée à une réécriture: au lieu de la refaire, améliorez l'existant.

Certainement peut-être: Le remplacement des structures de données et des algorithmes est en quelque sorte un cas limite. La différence décisive ici à l'OMI réside dans les petites étapes: être prêt à livrer, être prêt à travailler sur un autre cas.


Exemple: Imaginez que vous avez un module Randomizer de rapport qui est ralenti par son utilisation d'un vecteur. Vous avez indiqué que les insertions vectorielles sont le goulot d'étranglement, mais malheureusement, le module s'appuie sur la mémoire contigüe à de nombreux endroits de sorte que lors de l'utilisation d'une liste, les choses se brisent silencieusement.

Réécrire reviendrait à jeter le module d'un bâtiment de meilleure qualité et plus rapide à partir de zéro, en choisissant simplement quelques morceaux de l'ancien. Ou écrivez un nouveau noyau, puis insérez-le dans la boîte de dialogue existante.

Refactoring signifierait de prendre de petites mesures pour supprimer l'arithmétique pointeur, de sorte que le commutateur. Peut-être que vous créez même une fonction utilitaire enveloppant l'arithmétique du pointeur, remplaçant la manipulation directe du pointeur par des appels à cette fonction, puis passez à un itérateur afin que le compilateur se plaint des endroits où l'arithmétique du pointeur est toujours utilisée, puis basculez vers un list, puis supprimez la fonction ultilité.


L'idée derrière est que le code empire tout seul. Lors de la correction de bogues et de l'ajout de fonctionnalités, la qualité diminue par petites étapes - la signification d'une variable change subtilement, une fonction obtient un paramètre supplémentaire qui rompt l'isolement, une boucle devient un peu complexe, etc. Aucun de ceux-ci n'est un vrai bogue, vous pouvez ne dites pas un nombre de lignes qui rend la boucle complexe, mais vous nuisez la lisibilité et la maintenance.

De même, la modification d'un nom de variable ou l'extraction d'une fonction ne sont pas des améliorations tangibles en soi. Mais ensemble, ils combattent la lente érosion.

Comme un mur de galets où l'on tombe tous les jours au sol. Et tous les jours, un passant le ramasse et le remet.

18
peterchen

Avec la définition de Martin Fowler à l'esprit,

Le refactoring est une technique disciplinée pour restructurer un corps de code existant, en modifiant sa structure interne sans changer son comportement externe.

... Je pense que vous avez clairement raison.

Ils ont également suggéré des choses telles que la modification des structures de données (comme une Java LinkedList en une ArrayList), la modification des algorithmes (en utilisant le tri par fusion au lieu du tri par bulles), et même la réécriture de gros morceaux de code comme refactoring.

Changer un algorithme en quelque chose de beaucoup plus rapide n'est évidemment pas une refactorisation, car le comportement externe est changé! (Là encore, si l'effet n'est jamais perceptible, vous pourriez peut-être l'appeler refactoring après tout - et aussi optimisation prématurée. :-)

Ceci est ma bête noire; c'est ennuyeux quand les gens utilisent le terme avec négligence - j'en ai même rencontré quelques-uns qui pourraient utiliser négligemment refactoring pour pratiquement n'importe quel type de changement ou de correction. Oui, c'est un mot à la mode et cool, mais il n'y a rien de mal avec de vieux termes simples comme changement, réécriture ou amélioration des performances. Nous devons les utiliser le cas échéant et réserver refactoring pour les cas où vous améliorez vraiment la structure interne de votre logiciel. Au sein d'une équipe de développement, en particulier, il est important d'avoir un langage commun pour avec précision discuter de votre travail.

12
Jonik

Je pense que vous avez raison, mais discuter de la signification d'un mot n'est pas particulièrement intéressant ou productif.

8
cbp

Si l'interface avec un morceau de code change, je considère cela plus que du refactoring.

Le cas typique de refactoring est

  • "Oh, tous mes tests unitaires fonctionnent, mais je pense que mon code pourrait être rendu plus propre"
  • Changer le code pour être plus lisible/plus clair/efficace
  • Relancez les tests unitaires (sans modifier les tests) et vérifiez qu'ils fonctionnent toujours

Cela signifie que le terme refactoring est relatif à l'interface dont vous parlez. c'est-à-dire que vous pourriez refactoriser le code derrière une interface, tout en modifiant plus profondément le code d'une autre à un niveau inférieur (peut-être que cette distinction est ce qui crée la confusion entre vous et vos collègues ici?)

8
DanSingerman

http://en.wikipedia.org/wiki/Code_refactoring

Le refactoring de code est le processus de modification de la structure interne d'un programme informatique sans modifier son comportement fonctionnel externe ou la fonctionnalité existante, afin d'améliorer les propriétés internes non fonctionnelles du logiciel, par exemple pour améliorer la lisibilité du code, simplifier la structure du code, changer le code pour adhérer à un paradigme de programmation donné, pour améliorer la maintenabilité, pour améliorer les performances ou pour améliorer l'extensibilité.

J'accepte que la refactorisation du code inclut la rupture du code existant. Assurez-vous simplement que vous avez des tests unitaires afin de ne pas introduire de bogues et que le reste du code se compile. L'utilisation d'outils de refactorisation comme Resharper pour C # rend cela si facile!

  • Rendre le code plus compréhensible
  • Nettoyer le code et le rendre plus ordonné
  • Suppression du code! Le code et les commentaires redondants et inutilisés doivent être supprimés
  • Amélioration des performances
  • Rendre quelque chose de plus générique. Commencez avec la chose la plus simple possible, puis refactorisez-la pour la rendre plus facile à tester/isoler ou générique afin qu'elle puisse fonctionner de différentes manières grâce au polymorphisme
  • Garder le code DRY - Ne vous répétez pas, donc une session de refactoring peut impliquer de prendre du code répété et de le refactoriser en un seul composant/classe/module.
4
superlogical

I en désaccord :

En génie logiciel, "refactoriser" le code source signifie l'améliorer sans modifier ses résultats globaux [...]

Vous connaissez déjà les termes plus précis utilisés pour les sous-ensembles de refactoring, et oui, c'est un terme très général.

2
l0b0

Je pense que personne ne peut bénéficier d'une définition trop forte du terme "refactoring". La frontière entre la façon dont vous le percevez et vos collègues est floue et peut être plus proche de leur ou de votre point de vue en fonction de nombreux faits. Puisqu'il est dynamique, essayons de le définir. Définissez tout d'abord les limites du système ou du sous-système que vous essayez de refactoriser.

S'il s'agit d'une méthode, gardez le nom, les arguments d'entrée, le type de la valeur renvoyée et éventuellement les instructions de lancement fixes. Appliquez toutes les modifications à l'intérieur de la méthode sans changer la façon dont elle est vue à l'extérieur.

Si vous refactorisez une classe, corrigez son API publique et utilisez des variables de renommage, les méthodes d'extraction et toutes les autres techniques disponibles changent la classe pour qu'elle soit plus lisible et/ou plus performante.

Si la partie du code que vous refactorisez est un package ou un module, effectuez la refactorisation à l'intérieur de celui-ci en renommant éventuellement les classes, en supprimant, en introduisant des interfaces, en poussant/tirant du code dans des super/sous-classes.

1
Boris Pavlović

Refactoring = amélioration des exigences non fonctionnelles tout en gardant inchangées les exigences fonctionnelles.

Exigences non fonctionnelles = modularité, testabilité, maintenabilité, lisibilité, séparation des préoccupations, principes liskov et ainsi de suite ...

0
EnzoVici