Dans mon travail, je suis chargé d'améliorer la qualité du code. Pour assumer cette responsabilité, je jumelle souvent le programme avec des développeurs et anime des sessions sur les principes et les modèles de conception. J'ai été surpris par un développeur qui a dit qu'il devait violer le principe de responsabilité unique ou YAGNI.
principe de responsabilité unique indique que chaque classe ou module doit avoir une raison de changer. Pour le reformuler pour plus de clarté. Un module doit être responsable devant un et un seul acteur.
Selon moi, la raison pour laquelle nous suivons le principe de responsabilité unique est la suivante: si une classe est responsable de deux acteurs, et si un acteur entraîne un changement dans cette classe, il est possible de modifier involontairement les exigences de l'autre acteur.
YAGNI acronyme pour Vous n'en aurez pas besoin. Le principe de programmation extrême stipule que faire la chose la plus simple qui pourrait éventuellement fonctionner. Je me suis souvent appuyé sur YAGNI pour simplifier mon code en supprimant la modularisation inutile.
Conflit entre SRP et YAGNI: Nous avions un flux de travail implémenté dans le système. Et il était nécessaire de collecter des données sur l'utilisation de Work Flow. Nous devions connaître la répartition en pourcentage des paramètres utilisés dans le flux de travail. J'ai expliqué que la collecte de données doit se faire dans une autre classe (et nous pourrions éventuellement utiliser un modèle d'observateur ou de décorateur) et non dans une classe où le flux de travail est mis en œuvre car les deux exigences sont dictées par différents acteurs du système. Le flux de travail sert l'utilisateur final et la collecte de données sert la gestion des produits.
Mon collègue voulait enregistrer directement le paramètre de la classe où le workflow est implémenté. Il m'a dit que c'était la chose la plus simple à faire pour le faire fonctionner.
Je suis presque certain que dans ce scénario, je dois suivre SRP ici. Et je suis SRP (implémentation en 2 classes) parce que s'il y a un changement dans le flux de travail, la probabilité de modifier accidentellement la collecte de données est faible. Et quand il y a un changement dans la collecte des données, la probabilité de modifier accidentellement le flux de travail est également faible. Mais quand j'explique le changement possible dans le flux de travail ou la collecte de données, il me dit "tu n'en auras pas besoin".
Une suggestion sur la façon dont cela pourrait être expliqué?
YAGNI signifie éviter d'investir des efforts dans les changements de code pour les exigences hypothétiques qui peuvent arriver plus tard, et se concentrer plutôt sur les exigences actuelles. Mais cela ne se limite pas aux exigences fonctionnelles - tant que l'on ne crée pas de logiciel "à utiliser une fois puis jeter", il y a toujours l'exigence non fonctionnelle de garder le code lisible, compréhensible et évolutif. Et la séparation des responsabilités est l'un des principaux outils pour atteindre cet objectif.
Par conséquent, j'interprète le principe YAGNI dans une telle situation comme une recommandation de ne pas séparer les responsabilités avant que les avantages du PÉR ne deviennent visibles , ce qui est en fait dès que le code devient alambiqué . Et cela se produit généralement très rapidement lorsque l'on essaie d'implémenter différentes exigences métier dans une classe.
Je le tolérerais si je devais faire deux ou trois petites extensions à la classe "où le workflow est implémenté" pour ajouter cette exigence de journalisation. Mais alors mon niveau de douleur serait probablement atteint, et je commencerais à penser "diable, pouvons-nous refactoriser ce mécanisme de journalisation, ou au moins la collecte de données hors de la classe de workflow vers un meilleur endroit" .
Donc, au lieu de dire à vos développeurs:
dis leur:
Cela devrait être la justification dont on a besoin pour appliquer le SRP non pas pour une exigence inconnue à l'avenir, mais pour l'exigence de garder le code compréhensible maintenant.
Bien sûr, le seuil où le code est perçu comme alambiqué, et quand le SRP pourrait être appliqué pour résoudre ce problème peut varier d'un développeur à l'autre, mais c'est l'équilibre que votre équipe doit trouver, et où niquement les revues de code peut aider .
Ils sont là pour illustrer une connaissance acquise par l'expérience. Leur objectif est de donner un aperçu des différents aspects du développement logiciel. Il n'est pas surprenant qu'ils puissent parfois travailler les uns contre les autres.
J'aime vraiment cet article Medium qui présente la pyramide suivante:
L'idée derrière la pyramide est que vous ne devez pas miner les couches inférieures au détriment des couches supérieures.
Tout d'abord, ne pensez pas que cette image soit absolue. Il ne faut pas le suivre aveuglément. Mais il indique essentiellement qu'il est raisonnable que certains principes soient plus importants que d'autres - et peut-être, faire fonctionner votre solution devrait être la plus haute priorité.
Dans les lentilles de votre question SRP vs YAGNI, je me demanderais:
Il y a un très bon discours de Sandi Metz, auteur de "Practical Object-Oriented Design in Ruby" qui touche un très bon point:
La duplication est beaucoup moins chère que la mauvaise abstraction
Peut-être que développer une abstraction en ce moment peut vous amener à l'abstraire de la mauvaise façon.
Au final, les principes doivent être appliqués compte tenu du contexte de votre candidature. Il pourrait en effet être le cas où SRP serait le bon guide pour votre situation. Ou ... SRP peut vous amener à le sur-concevoir pour le moment et utiliser le YAGNI serait mieux adapté.
La principale conclusion devrait peut-être être que oui, il est courant que les principes se contredisent s'ils sont suivis religieusement et ce n'est pas un problème . Tout va bien.
Si quoi que ce soit, il est conseillé de commencer par implémenter une solution de travail (c'est-à-dire, choisir l'une des solutions et simplement aller avec) et de s'adapter aux implications qui en découlent, en construisant le logiciel de manière organique.
Le principe de responsabilité unique stipule que chaque classe ou module doit avoir une raison de changer. Pour le reformuler pour plus de clarté. n module doit être responsable devant un et un seul acteur.
Je ne suis pas d'accord avec cette affirmation, c'est-à-dire que je ne pense pas que ce soit une bonne règle à suivre. Et je sais que l'oncle Bob dit encore quelque chose de très similaire ou même identique.
Réfléchissons un peu à cela. Disons que j'ai une classe Amount
simple qui a add()
et subtract()
. Et si un "acteur" n'a besoin que de add()
et d'un autre subtract()
. Dois-je diviser cette classe? Cela rendrait-il le code plus facile à maintenir? Je ne pense pas que vous diriez que ce serait le cas.
Avant de dire que tout dépend et que vous devez toujours faire preuve de jugement, je ne pense pas qu'une règle soit une bonne règle lorsqu'elle ne s'applique même pas dans des cas simples.
À votre question: Vous avez en quelque sorte raison, j'essaierais également de séparer la collecte de données de la mise en œuvre réelle du flux de travail si cela est possible. Par exemple, si Workflow
est une interface et que toutes les données de la collecte de données sont disponibles via les paramètres, je créerais une implémentation qui collecte des données et délègue les appels.
S'il y a cependant des données internes au flux de travail qui doivent être collectées, qui ne sont pas disponibles via les paramètres publics, alors je n'aurais aucun problème à placer la collecte de données dans l'implémentation elle-même, car dans ce cas, je suis réellement intéressé par l'implémentation réelle. comportement.
Donc tu n'as pas encore réussi à me convaincre non plus :)