Une introduction rapide au refactoring pour les débutants
Marta Swiatkowska
Junior Software Engineer
J'écris peut-être sur quelque chose d'évident pour beaucoup, mais peut-être pas pour tout le monde. Le remaniement est, je pense, un sujet compliqué parce qu'il implique de modifier le code sans affecter son fonctionnement.
Il est donc incompréhensible pour certains que remaniement est en fait un domaine de la programmation, et c'est aussi une partie très importante du travail du programmeur. Le code est en constante évolution, il sera modifié tant qu'il sera possible d'ajouter de nouvelles fonctionnalités. Cependant, il peut prendre une forme qui ne permet plus d'ajouter efficacement de nouvelles fonctionnalités et il serait alors plus facile de réécrire l'ensemble du programme.
Qu'est-ce que le refactoring ?
Habituellement, la réponse que vous entendez est qu'il s'agit de changer la structure du code en appliquant une série de transformations de refactoring sans affecter le comportement observable du code. Cela est vrai. Récemment, je suis également tombé sur une définition de Martin Fowler dans son livre "Améliorer la conception des codes existants". où il décrit remaniement comme "faire de grands changements à petits pas". Il décrit remaniement comme une modification du code n'affectant pas son fonctionnement, mais il insiste sur le fait que cela doit se faire par petites étapes.
Le livre préconise également que remaniement n'affecte pas le fonctionnement du code et souligne qu'elle n'a aucun effet sur la réussite des tests à tout moment. Il décrit étape par étape comment effectuer en toute sécurité remaniement. J'ai aimé son livre parce qu'il décrit des astuces simples qui peuvent être utilisées dans le travail de tous les jours.
Pourquoi avons-nous besoin d'un remaniement ?
Le plus souvent, vous pouvez en avoir besoin lorsque vous souhaitez introduire une nouvelle fonctionnalité et que le code dans sa version actuelle ne le permet pas ou qu'il serait plus difficile de le faire sans modifier le code. Elle est également utile lorsque l'ajout de nouvelles fonctionnalités n'est pas rentable en termes de temps, c'est-à-dire qu'il serait plus rapide de réécrire le code à partir de zéro. Je pense que l'on oublie parfois que remaniement peut rendre le code plus propre et plus lisible. Martin écrit dans son livre qu'il effectue des remaniements lorsqu'il sent des odeurs désagréables dans le code et, comme il le dit lui-même, qu'il n'y a pas d'autres solutions que de remanier le code, "Il laisse toujours de la place pour le meilleur. Et il m'a surpris ici en considérant le refactoring comme un élément du travail quotidien du code. Pour moi, les codes sont souvent incompréhensibles, les lire est un peu une expérience car le code est souvent peu intuitif.
La caractéristique d'un programme bien conçu est sa modularitéGrâce à la modularité, il suffit de ne connaître qu'une petite partie du code pour introduire la plupart des modifications. La modularité facilite également l'intégration de nouvelles personnes et leur permet de travailler plus efficacement. Pour parvenir à cette modularité, les éléments de programme apparentés doivent être regroupés, les connexions étant compréhensibles et faciles à trouver. Il n'existe pas de règle empirique unique sur la manière d'y parvenir. Au fur et à mesure que l'on comprend mieux comment le code est censé fonctionner, on peut regrouper les éléments, mais il faut aussi parfois tester et vérifier.
L'une des règles du refactoring en YAGNIIl s'agit d'un acronyme pour "You Aren't Gonna Need It" (vous n'en aurez pas besoin), qui provient de l'anglais "You Aren't Gonna Need It". Programmation eXtreme (XP) utilisé principalement dans Agiledéveloppement de logiciels équipes. Pour faire court, YAGNI dit qu'il ne faut faire que les choses les plus récentes. Cela signifie essentiellement que même si quelque chose peut être nécessaire à l'avenir, il ne faut pas le faire tout de suite. Mais nous ne pouvons pas non plus bloquer les extensions futures et c'est là que la modularité devient importante.
Lorsque l'on parle de remaniementil convient de mentionner l'un des éléments les plus essentiels, à savoir les tests. En effet, en remaniementNous avons besoin de savoir que le code fonctionne toujours, parce que remaniement ne modifie pas son fonctionnement, mais sa structure, de sorte que tous les tests doivent être réussis. Il est préférable d'exécuter des tests pour la partie du code sur laquelle nous travaillons après chaque petite transformation. Cela nous permet de confirmer que tout fonctionne comme il se doit et de réduire la durée de l'ensemble de l'opération. C'est ce dont Martin parle dans son livre : exécuter des tests aussi souvent que possible afin de ne pas prendre du recul et perdre du temps à chercher une transformation qui a cassé quelque chose.
Refonte du code Sans test, c'est un vrai calvaire et il y a de grandes chances que quelque chose ne fonctionne pas. Si c'est possible, il serait préférable d'ajouter au moins quelques tests de base qui nous donneront un peu d'assurance que le code fonctionne.
Les transformations énumérées ci-dessous ne sont que des exemples, mais elles sont très utiles dans la programmation quotidienne :
Extraction de fonctions et de variables - si la fonction est trop longue, vérifiez s'il existe des fonctions mineures qui pourraient être extraites. Il en va de même pour les longues lignes. Ces transformations peuvent aider à trouver des doublons dans le code. Grâce aux petites fonctions, le code devient plus clair et plus compréhensible,
Renommer les fonctions et les variables - l'utilisation d'une convention de dénomination correcte est essentielle à une bonne programmation. Les noms de variables, lorsqu'ils sont bien choisis, peuvent en dire long sur le code,
Regroupement des fonctions dans une classe - cette modification est utile lorsque deux classes effectuent des opérations similaires, car elle permet de raccourcir la durée de la classe,
Remplacer l'instruction imbriquée - si la condition est vérifiée dans un cas particulier, émettre une instruction de retour lorsque la condition se produit. Ce type de test est souvent appelé clause de garde. Le remplacement d'une instruction conditionnelle imbriquée par une instruction de sortie modifie l'importance du code. La construction if-else attribue la même importance aux deux variantes. Pour la personne qui lit le code, il s'agit d'un message indiquant que chacune d'entre elles est aussi probable et importante que l'autre,
Introduction d'un cas spécial - si vous utilisez certaines conditions à plusieurs reprises dans votre code, il peut être intéressant de créer une structure distincte pour elles. Par conséquent, la plupart des vérifications de cas particuliers peuvent être remplacées par de simples appels de fonction. Souvent, la valeur courante qui nécessite un traitement spécial est null. C'est pourquoi ce modèle est souvent appelé "objet zéro". Toutefois, cette approche peut être utilisée dans n'importe quel cas particulier,
Remplacement du polymorphisme d'instruction conditionnelle.
Exemple
Il s'agit d'un article sur remaniement et un exemple est nécessaire. Je veux montrer un exemple simple de refactorisation ci-dessous avec l'utilisation de Remplacer la déclaration imbriquée et Remplacement du polymorphisme d'instruction conditionnelle. Supposons que nous ayons une fonction de programme qui renvoie un hachage contenant des informations sur la manière d'arroser les plantes dans la vie réelle. Ces informations se trouveraient probablement dans le modèle, mais pour cet exemple, elles se trouvent dans la fonction.
def watering_info(plant)
result = {}
if plant.is_a ? Suculent || plant.is_a ? Cactus
result = { water_amount : "Un petit peu" , how_to : "Par le bas", durée de l'arrosage : "2 semaines" }
elsif plant.is_a ? Alocasia || plant.is_a ? Maranta
result = { water_amount : "Grande quantité", how_to : "Comme vous préférez", watering_duration : "5 jours" }
elsif plant.is_a ? Peperomia
result = { water_amount : "Quantité d'eau",
how_to : "Par le bas ! ils n'aiment pas l'eau sur les feuilles",
watering_duration : "1 semaine" }
else
result = { water_amount : "Quantité d'eau",
how_to : "Comme vous le souhaitez",
watering_duration : "1 semaine"
}
fin
retourner le résultat
fin
L'idée est de changer pour revenir :
if plant.isa ? Suculent || plant.isa ? Cactus
result = { wateramount : "Un peu" , howto : "Depuis le bas",
Pour
return { water_amount : "Un petit peu" , how_to : "Depuis le bas",watering_duration : "2 semaines" } if plant.is_a ? Suculent || plant.is_a ? Cactus
return { eaumontant : "Un peu", commentà : "De bas en haut", arrosagedurée : "2 semaines" } if plant.isa ? Suculent || plant.is_a ? Cactus
Et ainsi de suite, jusqu'à ce que nous arrivions à une fonction qui ressemble à ceci :
def watering_info(plant)
return result = { wateramount : "Un petit peu" , howto : "Depuis le bas", durée de l'arrosage : "2 semaines" } if plant.isa ? Suculent || plant.is_a ? Cactus
return result = { wateramount : "Grande quantité", howto : "Comme vous le souhaitez", wateringduration : "5 jours" } if plant.isa ? Alocasia || plant.is_a ? Maranta
return result = { water_amount : "Quantité d'eau",
howto : "Par le bas ! ils n'aiment pas l'eau sur les feuilles",
wateringduration : "1 semaine" } if plant.is_a ? Peperomia
return result = { water_amount : "Quantité d'eau",
how_to : "Comme vous le souhaitez",
watering_duration : "1 semaine"
}
fin
À la toute fin, nous avions déjà un résultat de retour. Une bonne habitude consiste à procéder étape par étape et à tester chaque changement. Vous pourriez remplacer ce bloc if par un switch case et cela aurait immédiatement l'air mieux, et vous n'auriez pas à vérifier tous les ifs à chaque fois. Cela ressemblerait à ceci :
def watering_info(plant)
swich plant.class.to_string
case Suculente, Cactus
{ wateramount : "Un peu" , howto : "Par le bas", watering_duration : "2 semaines" }
cas Alocasia, Maranta
{ wateramount : "Grande quantité", howto : "Comme vous le souhaitez", watering_duration : "5 jours" }
cas Peperomia
{ water_amount : "Quantité d'eau",
how_to : "Par le bas ! ils n'aiment pas l'eau sur les feuilles",
watering_duration : "1 semaine" }
else
{ water_amount : "Dicent amount",
how_to : "Comme vous préférez",
watering_duration : "1 semaine" }
fin
fin
Vous pouvez ensuite appliquer le Remplacement du polymorphisme d'instruction conditionnelle. Il s'agit de créer une classe avec une fonction qui renvoie la bonne valeur et qui les place à l'endroit approprié.
classe Suculente
...
def watering_info()
return { quantité d'eau : "Un petit peu" , howto : "Depuis le bas", watering_duration : "2 semaines" }
fin
fin
classe Cactus
...
def watering_info()
return { quantité d'eau : "Un petit peu" , howto : "Depuis le bas", watering_duration : "2 semaines" }
fin
fin
classe Alocasia
...
def watering_info
return { wateramount : "Grande quantité", howto : "Comme vous le souhaitez", watering_duration : "5 jours" }
fin
fin
classe Maranta
...
def watering_info
return { wateramount : "Grande quantité", howto : "Comme vous le souhaitez", watering_duration : "5 jours" }
fin
fin
classe Peperomia
...
def watering_info
return {montant_d'eau : "Montant de l'arrosage",
how_to : "Par le bas ! ils n'aiment pas l'eau sur les feuilles",
watering_duration : "1 semaine" }
fin
fin
classe Plant
...
def watering_info
return {montant_d'eau : "Montant de l'arrosage",
how_to : "Comme vous préférez",
watering_duration : "1 semaine" }
fin
fin
Et dans la fonction principale watering_infofunction, le code ressemblera à ceci :
def watering_info(plant)
plant.map(&:watering_info)
end
Bien entendu, cette fonction peut être supprimée et remplacée par son contenu. Avec cet exemple, j'ai voulu présenter le fonctionnement général de la fonction modèle de remaniement.
Résumé
Refonte est un vaste sujet. J'espère que cet article vous a donné envie d'en savoir plus. Ces articles compétences en matière de refonte vous aider à attraper vos bogues et à améliorer votre atelier de code propre. Je recommande la lecture du livre de Martin (Improving the Design of Existing Code), qui est un ensemble de règles assez basiques et utiles de remaniement. L'auteur montre différentes transformations étape par étape avec une explication complète et une motivation, ainsi que des conseils pour éviter les erreurs. remaniement. Grâce à sa polyvalence, c'est un livre idéal pour les frontend et les . développeurs backend.