GLO-4002 - Site du cours 2023

Réusinage : extraction de méthodes

Contexte

L'un des réusinages les plus fréquents consiste à revoir la structure interne d'une classe afin de corriger un problème ou encore d'introduire une nouvelle fonctionnalité. Bien entendu, dans le meilleur des mondes, nous n'aurons qu'à créer une autre implémentation (Open-Closed Principle), mais il est impossible de toujours respecter ce principe.

Que l'on veuille ou non, nous devrons tôt ou tard modifier du code existant. Heureusement, avec une encapsulation digne de son nom et des tests suffisamment précis, ces changements sont normalement les réusinages les plus simples à opérer. En d'autres mots, les commandes que nous verrons dans la présente capsule seront principalement utiles lorsque viendra le temps de modifier la structure interne/privée d'une classe/fonction. Bien entendu, avant d'entreprendre un quelconque changement, nous devrons toujours nous assurer que tous les comportements de la classe en question soient testés.

Voici une liste des commandes que je considère les plus utiles dans ce genre de situation :

L'exercice proposé ici portera principalement sur l'extraction de méthodes, mais gardez en tête les autres raccourcis qui sont tous autant importants dans l'atteinte de la maîtrise de votre environnement de travail.

Exercices

Pour cet exercice, utilisez le dépôt github officiel-refactoring-extract-method.

  1. En partant de la classe ExtractToMethod,
    1. cherchez à créer l'implémentation interne la plus propre.
    2. Sur quels critères basez-vous votre réflexion pour déterminer la propreté du code ?
    3. Nommer un avantage de votre nouveau code par rapport à celui d'origine.
    4. Nommer un désavantage de votre nouveau code par rapport à celui d'origine.
  2. Toujours en partant de la classe ExtractToMethod, faites un réusinage dans lequel vous n'aurez que deux méthodes privées en conservant l'ensemble des opérations sur la liste (basket.add et basket.remove), soit :
    • private void addVeggies(List<String> basket)
    • private void removeFruits(List<String> basket)
  3. À partir du résultat de la question 2, modifiez le contenu des méthodes addVeggies et removeFruits pour en faire des one-liners.
  4. À partir du résultat de la question 3, débarrassez-vous des méthodes privées.
  5. Repartez désormais de la classe ExtractToMethod et proposez une implémentation avec une seule méthode privée qui se nommera swap.
  6. Avant de faire les exercices 2 à 5, aviez-vous considéré l'ensemble de ces implémentations alternatives ?
  7. Considérez-vous toujours votre réusinage de l'exercice 1 comme la meilleure proposition ?
    1. Si oui, pourquoi ?
    2. Sinon, laquelle est désormais celle que vous préférez ?

Réflexion

Lorsque j'étais étudiant dans le cours de qualité métrique, j'aurais probablement eu une opinion tranchée sur la meilleure implémentation. J'aurais probablement basé mon argumentaire sur le nombre de lignes de code (LOC) ou la complexité cyclomatique d'une approche par rapport à une autre ou encore sa configurabilité pour permettre d'ajouter ou enlever des cas. Si tel est votre cas, je crois qu'il s'agit d'excellents réflexes que je vous encourage à continuer à mettre en pratique.

Cependant, le développeur que je suis devenu ne saurait prendre une décision dans le contexte présent en raison de la nature de l'exercice. Je crois que la vraie réponse ici est: Ça dépend du contexte !

En effet, il existe plusieurs façons d'organiser le code. Plus souvent qu'autrement, certaines approches seront définitivement préférables à d'autres, mais il arrivera aussi que des approches qui soient "équivalentes" en termes de propreté de code (Clean Code).

Parmi ces approches considérées équivalentes, certaines présenteront parfois des avantages par rapport à d'autres quant à la maintenabilité future du code. Il faut toutefois considérer le contexte du projet et avoir des connaissances du domaine d'affaires pour prendre cette décision.

Il arrivera aussi certaines situations qui reviendront à une pure question de préférences personnelles. Ne soyez pas surpris de rencontrer des gens dans votre parcours qui seront plus ou moins ferme à ce niveau de détails. Je vous encourage toutefois à éviter de tomber dans le piège des réusinages sans valeur et des revues de code de complaisance. Il vaut mieux favoriser l'uniformité et l'impersonnel à l'unicité et la personnalité.

La qualité du code dépasse largement les principes énoncés dans Clean Code bien que ceux-ci restent importants. Ceci est d'autant plus vrai lorsqu'il est question de l'implémentation concrète d'une classe. Je parle ici de la structure interne d'une classe/fonction qui ne sera pas exposée à ses clients.

Garder en tête que le code est une plasticine qui ne cessera jamais d'être remodelée. Utiliser les réusinages à bon escient et non pas seulement pour satisfaire une envie pressante de tout refaire à votre main. Des décisions précipitées peuvent "cacher" des designs plus optimaux ou la profondeur d'un domaine d'affaires qui mèneront à des avancées majeures.

En ce qui concerne la règle du boyscout, cette dernière s'applique à tout bout de code toucher par vos changements et non pas à tout le projet à chacun de vos commits. Bien entendu, il vaut mieux répliquer vos changements partout où ceux-ci s'appliquent afin de conserver l'uniformité dans le code si une telle uniformité existe. Ceci dit, dès que vous voyez qu'un tel ménage prend des proportions qui dépassent le terrain de jeu initial, il est souvent préférable d'annuler vos changements pour les faire dans une merge request spécifique. En effet, certains ménages inoffensifs à première vue peuvent occasionner des problèmes s'ils ne sont pas faits au moment opportun comme le renommage de masse ou le déplacement de fichiers.

Un autre problème avec les réusinages de code consiste à ne pas avoir les tests nécessaires en place pour s'assurer qu'on ne brise rien. Imaginez vous briser la production en raison d'un réusinage esthétique sans valeur ajoutée pour l'utilisateur et de devoir expliquer l'incident à votre patron...

Finalement, le réusinage de code patrimonial est un marathon et non un sprint. Il s'agit d'un travail de longue haleine qui sera fait une bouchée à la fois.

Sommaire

  • Le Clean Code, c'est important, mais la qualité logicielle ne s'arrête pas là.
    • Il faut considérer le contexte du projet pour déterminer l'implémentation qui est la plus appropriée.
  • Le boyscout rule se limite aux fichiers touchés par vos changements.
    • Faire attention aux BIG BANG refactors accidentels.
  • Évitez de tomber dans les préférences personnelles.
    • Uniformité + Impersonnel > Unicité + Personnalité
  • Le réusinage de code patrimonial est un marathon et non un sprint.
  • Raccourcis :
    • ALT+SHIFT+UP_ARROW : déplacer les lignes vers le haut
    • ALT+SHIFT+DOWN_ARROW : déplacer les lignes vers le bas
    • CTRL+ALT+M : extraire une méthode
    • CTRL+ALT+C : extraire une constante
    • CTRL+ALT+V : extraire une variable
    • CTRL+ALT+F : extraire une propriété
    • CTRL+ALT+P : extraire un paramètre
    • CTRL+ALT+N : mise en ligne
    • CTRL+F6 : modifier la signature

Solution

Une solution potentielle est fournie dans la branche solution du même dépôt github.