Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Le code qui pue 3 – Ballonnement des composants

Ah! Une application complète, un module, une classe ou même une méthode peut avoir des ballonnements. Évidemment, ça leur donnent des gaz qui font qu’ils puent 😂. Pour ce troisième article de la série sur les code smells, je vous présente une problématique commune qu’on voit dans tout bon système.

Aujourd’hui, on traite des ballonnements (bloaters).

C’est quoi le problème?

Premièrement, définissons le terme ballonnement ou bloater. Le ballonnement est une caractéristique du code qui apparaît habituellement après plusieurs mois de développement. Ça regroupe plusieurs sous-catégories, dont le fameux trop de paramètres traité lors du dernier article.

Le ballonnement se définit par une croissance démesurée dans la taille d’un composant (classe, méthode, module, etc.). Comme vous le savez probablement, la taille limite d’un composant est assez subjective. Selon certains, une méthode devrait avoir au maximum 5-6 lignes de code (Martin Fowler), ou suivre la règle des 30 (Martin Lippert and Stephen Roock), voire même trois lignes comme je m’amuse à dire en formation.

Mais Sylvain, c’est impossible 😅

Bien sûr que non! Mais je t’explique, tu vas tout comprendre.

C’est causé par un comportement humain

Où il y a de l’homme, il y a de l’hommerie. Il y a plusieurs causes possibles au ballonnement, mais commençons par les plus simples :

Le nom de la classe contient un mot trop vague

Sur le coup, ça peut sembler une bonne idée, mais ce qui différencie un bon programmeur d’un autre, c’est ça capacité à penser à l’évolutivité et au support. Donc initialement, lorsqu’on crée une classe  DateUtils, UserHelper ou InvoiceManager pour ne prendre que ces exemples, elle a 3-4 méthodes de 2-3 lignes. On se dit : « Parfait! Une belle classe propre », sans vraiment se soucier que Utils, Helper ou Manager sont des mots qui attirent les fonctionnalités.

Vous êtes maintenant un autre dev. La personne qui a créé le DateUtils est partie chez un compétiteur parce que ses idées ne sont pas assez prises en compte et vous vous retrouver à devoir faire un calcul sur deux dates, tel que compter le nombre de jours entre deux. Facile, ça va dans le DateUtils.

La paresse intellectuelle (sans méchanceté)

Quelques mois/années plus tard, un autre dev joint l’équipe. Il doit ajouter une autre fonction de calcul de date, telle que trouver la date du prochain premier jeudi du mois. Il regarde DateUtils en pensant à Uncle Bob et se dit : « Humm… cette classe est rendue à 20 méthodes… ». C’est exactement à ce moment précis que ça se gâte.

C’est un comportement totalement humain. La classe est déjà trop grosse, donc c’est moins grave si j’en ajoute. C’est plus facile d’ajouter là que de continuer à chercher où ça va vraiment. Ça coûte moins cher de l’engraisser que de la réparer (vraiment?!)

Comment on répare ça?

Il n’y a malheureusement aucune règle d’or. Détrompez-vous, c’est normal d’avoir des classes utilitaires statiques. C’est aussi normal de voir des méthodes de 200 lignes, car certains algorithmes ne peuvent pas être explosés.

Par contre, je le répète, pensez à l’évolutivité et au support. Par exemple, si vous voulez calculer le nombre de jours entre deux dates, peut-être que votre langage vous offre une API pour ça? Est-ce que vous réutilisez réellement l’algorithme ou chacune des méthodes du DateUtils est appelée une seule fois dans le code? Vous l’avez mise dans DateUtils « au cas où », en pensant au principe DRY (Don’t Repeat Yourself) mais sans se soucier de YAGNI (You Ain’t Gonna Need It)? Peut être que ça devrait aller dans une classe DateSpanCalculator?

Donc l’étape 1, c’est de trouver où va vraiment ce code. Est-il utilisé à une seule place? Laissez l’algorithme directement dans le code, ou extrayez en méthode. Si quelqu’un en a besoin ailleurs, il trouvera où le mettre.

Si, par contre, c’est utilisé ailleurs, il faut voir où. Peut-être que vous vous rendrez compte que le calcul entre deux dates ne sert qu’à vérifier si une facture est en retard? Peut-être que le calcul devrait aller dans une méthode isPassedDue() de la classe Invoice ? Peut-être ailleurs?

Une fois que vous avez identifié où va la méthode, vous pourriez utiliser plusieurs techniques de refactoring :

Extraction de méthode : Si une méthode a plus de 3 lignes, essayer de séparer l’algorithme en étapes dans des méthodes différentes (détails).

Extraction de classe : Si une classe a plus de 30 méthodes, regroupez celles qui partagent des caractéristiques communes sous une même classe (détails).

Par exemple :

Conclusion

Bien sûr, on peut débattre pendant des heures à savoir quelle est la taille optimale des composants. Il faut idéalement faire appel à son gros bon sens et résister aux tentations de prendre des raccourcis intellectuels. Chaque décision que tu prends a des impacts sur l’évolutivité et la maintenance.

Si tu as apprécié cet article, prends une seconde pour le partager avec tes collègues 🙂 Sinon, tu peux aussi nous encourager en nous offrant une bière.

Cheers!

Suivez-nous par courriel!

Saisissez votre adresse courriel pour vous abonner au blog d'Ezo et recevoir une notification de chaque nouvel article par email.