Comprenez les pièges des solutions compliquées
Il existe une légende urbaine qui concerne les programmes spatiaux américain et soviétique, qui se base sur un problème simple : comment écrire dans l’espace, qui est un environnement sans aucune gravité ? 🤷♀️
Selon la légende, les Américains et les Soviétiques ont trouvé des réponses différentes :
Les ingénieurs américains ont travaillé à l’élaboration d’un stylo pressurisé, pour que l’encre puisse continuer à s’écouler.
Les Soviétiques, eux, ont opté pour un crayon à papier.
Vous pourriez, vous aussi, tomber dans le piège de l’ingénierie à outrance ! 😅
Voici comment vous pouvez rencontrer des problèmes. La première fonctionnalité est facile à écrire. Vous déterminez ce qui doit être fait, vous le concevez, et vous mettez le code en place.
Puis vient une autre fonctionnalité, qui ressemble beaucoup à la première – mais avec de petites différences. Comment allez-vous ajouter cette nouvelle fonctionnalité ? Eh bien, vous pouvez toujours copier-coller. Dupliquez simplement le code existant et modifiez-le légèrement pour traiter la nouvelle fonctionnalité !
Malheureusement, avec le temps, le résultat de cette pratique peut impliquer une grande quantité de code répété, qui devient difficile à maintenir. Le prochain développeur qui ajoutera ou modifiera une fonctionnalité devra alors modifier de nombreuses parties de votre code, et il risque d’oublier quelque chose. 😱
Alternativement, vous pourriez ajouter une nouvelle fonctionnalité en donnant plus de responsabilités à une classe ou une fonction déjà existante. Étant donné qu’elle fait déjà la plus grosse partie du travail, lui en donner un peu plus ne changerait pas grand-chose, non ?
Je ne vois pas où est le problème ! Pourquoi recommande-t-on si fortement d’éviter cela ?
Tout d’abord, la solution se complexifie progressivement. Vous risquez d’avoir moins de personnes qui comprennent comment elle fonctionne. Vous faites donc perdre du temps à de futurs développeurs, car ils doivent essayer de tout comprendre.
Deuxièmement, l’architecture devient fragile. Quand chaque morceau de code dépend de trop de morceaux différents, vous finissez par atteindre un stade où, pour réparer un petit détail, vous devrez revoir la conception de la moitié de votre application.
Imaginez que l’une des pédales de votre vélo se casse. Elle devrait être facile à remplacer – n’importe quel réparateur de vélo pourrait résoudre ce problème en quelques minutes ! Mais, si le vélo a été suffisamment mal conçu, avec trop d’interdépendances, vous risquez de ne pas avoir d’autre choix que de jeter vos deux pédales et l’ensemble du pignon. 😞
Comment éviter d’écrire des solutions qui vont trop loin ? Ou des solutions qui deviennent difficiles à comprendre et à modifier ?
Pensez au principe KISS (Keep It Simple, Stupid ou “garde ça simple, idiot”) ! 👶
Un avantage : votre solution sera plus facile à comprendre. Si elle est plus facile à comprendre, alors elle est plus facile à modifier. De plus, vous pouvez avoir davantage de certitude qu’une modification ne cassera rien.
Autre avantage, elle sera plus facile à tester. Mieux vos classes sont séparées, et plus il sera facile de les tester séparément.
Plus facile à dire qu’à faire ! Heureusement, vous pouvez capitaliser sur les connaissances de ceux qui vous ont précédé, et qui ont trouvé de meilleures façons de faire les choses ! Nous avons déjà vu cela avec les design patterns de la section précédente. Nous irons maintenant plus loin dans cette voie avec les principes de conception SOLID.
Identifiez les principes SOLID
Chacune des lettres de l’acronyme SOLID représente une excellente idée à garder à l’esprit lorsque vous construisez l’architecture de votre système. Nous examinerons chacune d’entre elles en profondeur au fil du cours. Nous les mettrons également en pratique en améliorant notre application de jeu de cartes. Commençons par une vue d’ensemble de ces idées clés :
« S » désigne la responsabilité unique (« Single responsibility »).
Chaque classe ou fonction doit faire une seule chose, et la faire bien. Elle ne doit avoir qu’une seule raison de changer.
« O » désigne le principe ouvert/fermé (« Open/Closed »).
Les classes doivent être ouvertes à l’extension, mais fermées à la modification.
Qu’est-ce que cela peut bien vouloir dire ?
Eh bien, dans l’idéal, il doit être facile d’ajouter un nouveau concept au système en étendant la fonctionnalité d’origine, sans dupliquer tout un tas de code. De plus, dans l’idéal, vous ne devriez pas avoir à apporter de modifications au code existant, dans l’aventure.
« L » désigne la substitution de Liskov.
Les sous-classes doivent pouvoir faire tout ce que font leurs classes parentes. Si vous remplacez une classe parente par l’une de ses sous-classes, cela ne doit pas casser votre système !
« I » désigne la ségrégation des interfaces (« Interface Segregation »).
Cela correspond essentiellement au principe de responsabilité unique, appliqué aux interfaces.
« D » désigne l’inversion des dépendances (« Dependency Inversion »).
Les classes parentes ne doivent pas avoir à changer lorsque l’une de leurs sous-classes est modifiée.
Comme nous le verrons, les design patterns que nous avons déjà abordés sont efficaces, car ils démontrent ces principes ! Nous les utiliserons pour améliorer encore davantage le jeu de cartes que nous avons écrit lorsque nous avons exploré le MVC.
En résumé
Les designs plus propres sont plus faciles à comprendre, maintenir, modifier, et tester.
Si l’on suit les principes SOLID, on obtient un design plus propre.
Les principes SOLID sont :
○ Responsabilité unique (« single responsibility »).
○ Principe ouvert/fermé (« open/closed »).
○ Substitution de Liskov.
○ Ségrégation des interfaces (« interface segregation »).
○ Inversion des dépendances (« dependency inversion »).
Rendez-vous au prochain chapitre, où nous commencerons à détailler ces principes – en commençant par le principe de responsabilité unique !