Vous avez découvert des approches efficaces pour concevoir vos développements. Envisageons à présent le processus inverse : les approches qui font empirer la situation. Celles que vous voulez à tout prix éviter. Elles sont également associées à un acronyme, à l'instar de SOLID. Il s'agit de l'acronyme STUPID. :)
La difficulté avec les concepts STUPID, c'est qu'ils semblent à priori recevables, à un moment donné. Au moins à court terme. Mais c'est leur maintenabilité à long terme qui fait défaut.
Évitez par conséquent de vous laisser tenter, et trouvez un moyen de les remplacer par un principe SOLID. La meilleure façon d'éviter les mauvaises habitudes, c'est d'en prendre conscience. Entrons dans le vif du sujet !
S pour Singleton
Un singleton veille à ce qu'une seule instance d'une classe soit créée, ce qui peut sembler judicieux. Par exemple, si vous devez activer des sons dans votre système, vous disposez probablement d'une seule classe SoundManager. Vous appelez SoundManager.playSound(), et ce dernier définit le volume, charge le fichier audio et lance le lecteur multimédia de la plateforme.
Le problème survient lorsque vous commencez à envisager la plupart des fonctionnalités sous cet angle. Il devient tentant de recourir à un singleton pour tout. Vous vous retrouvez ensuite contraint de créer ces maudites classes managers qui gèrent les connexions, les vidéos, les bases de données et bien plus encore.
Ce pattern finit alors par gérer des informations d'état. Ces informations sont des informations de modèle qui se retrouvent partagées dans l'ensemble des modules de votre application. En général, il n'est pas judicieux de rendre votre système dépendant de variables d'informations d'état. Le problème est que n'importe quelle partie du programme pourra modifier l'état. Reprenons l'exemple des sons. Votre code a paramétré le niveau du volume sur 4. Ensuite, d'autres parties du programme le paramètrent sur 11. Vous devez alors revenir en arrière et le reparamétrer sur 4. Ou vous devez identifier la partie fautive du code qui modifie le paramétrage de la valeur.
Quelle est la solution ?
Demandez-vous si vous avez vraiment besoin d'un singleton. Pourquoi cette classe doit-elle être accessible, comme s'il s'agissait de données générales ? S'il existe une seule raison pour qu'elle ne soit pas accessible à tous, c'est qu'elle ne doit pas être un singleton.
T pour Tight Coupling (couplage fort)
Voici le véritable problème d'un singleton : toute classe qui en utilise un est étroitement couplée à ce dernier. Cette classe ne peut être extraite et utilisée ailleurs. Elle emmène toujours le singleton avec elle.
Vous pouvez réduire le couplage en codant des interfaces plutôt que des implémentations. Vous avez vu précédemment qu'en couplant étroitement le contrôleur à la vue (quand le contrôleur a un accès direct aux widgets de la vue), la modification de la vue entraîne des modifications intempestives au niveau du contrôleur.
Cette même situation, être dépendant d'une implémentation concrète plutôt que d'une interface, peut se produire dans beaucoup de cas de figure. Soyez conscient du fait que les changements apportés à l'implémentation d'une classe peuvent entraîner des modifications sur d'autres classes qui dépendent d'elle.
U pour Untestability
Les raisons pour qu'une classe soit difficile ou impossible à tester sont nombreuses. Mais bien souvent, elle se résument à un couplage fort avec un autre composant. Si votre classe a de trop nombreuses dépendances, pensez à la réécrire. Un composant peut s'avérer difficile à tester lorsqu'il enfreint le principe de responsabilité unique et effectue trop de choses.
C'est pour cette raison que nous avons codé deux GameEvaluators (high et low) au lieu d'une seule méthode avec un paramètre booléen pour sélectionner l'un ou l'autre. Il est plus facile de tester une méthode qui ne fait qu'une seule chose.
P pour Premature Optimization (optimisation prématurée)
L'optimisation prématurée consiste à anticiper un problème avant qu'il ne devienne un problème.
Maiiiis, c'est une bonne chose, non ?!
Pas toujours ! Par exemple, pendant notre partie de cartes, nous avons besoin de mélanger le jeu. Nous utilisons le générateur de nombres aléatoires de Java pour créer des index et déplacer les cartes en permutant leurs positions.
Le mélange des cartes peut être un processus lent et répétitif. Nous pourrions concevoir un algorithme ultra-rapide. Il serait plus performant mais ne serait pas à la portée de tous. Et les cartes ne sont pas mélangées si souvent que cela. Cela ne vaut donc pas la peine d'optimiser cet algorithme.
I pour Indescriptive Naming (nommage non descriptif)
Si ce problème semble facile à éviter, il n'en reste pas moins qu'il survient assez fréquemment. Il survient, car au moment d'écrire le code, le problème et la solution semblent logiques. Imaginons que vous ayez un rectangle à gérer. Vous nommez les variables de l'angle supérieur gauche x1 et y1. Logique.
Quelques mois plus tard, lors d'une revue de code, vous (ou quelqu'un d'autre) voit ces variables. Qu'est-ce que x1 ? Vous devez lire le code pour le savoir. Si vous aviez appelé les variables upperLeftCornerX et upperLeftCornerY, vous auriez immédiatement su à quoi elles correspondaient.
D pour Duplication
La duplication est un piège dans lequel il est très facile de tomber. Vous devez ajouter une nouvelle fonctionnalité. Elle ressemble à une autre fonctionnalité, mais avec un fonctionnement légèrement différent. Que faites-vous ? Copier/coller/modifier. En répétant cette méthode, vous finissez par avoir du code dupliqué à de nombreux endroits. Si quelque chose de fondamental doit être modifié, toutes ces duplications doivent être identifiées et modifiées.
Alors, on ne doit jamais copier-coller ?
Les opérations de copier-coller sont parfaites pour mettre quelque chose en place en peu de temps. Mais vous devrez revenir sur votre code et trouver une meilleure solution qui soit viable à long terme. Posez-vous ces questions :
Pourquoi y a-t-il autant de similitudes entre ces deux implémentations ?
Le code dupliqué peut-il être placé dans une classe de base commune ?
Puis-je extraire une interface et placer les éléments légèrement différents dans des implémentations différentes ?
Il n'existe aucune réponse applicable à toute situation. L'essentiel ici est de se rappeler que toutes ces approches STUPID sont simples à mettre en œuvre. Elles semblent sensées sur le moment. Les problèmes qu'elles génèrent n'apparaissent que bien plus tard durant le projet.
En résumé
Signification de l'acronyme STUPID :
singleton ;
tight coupling (couplage fort) ;
untestability (impossibilité de tester le code) ;
premature optimization (optimisation prématurée) ;
indescriptive naming (nommage non descriptif) ;
duplication.
Les approches STUPID aboutissent à la conception de codes difficiles à gérer et à tester.
Il est facile de tomber dans le piège des approches STUPID. Vous devez donc rester vigilant et vous poser les bonnes questions, grâce à l'approche SOLID.
Testons à présent ce que vous avez appris des principes SOLID dans le quiz de fin de section. On se rejoint ensuite pour apprivoiser les design patterns !