Selon la documentation de Microsoft, l'article de SOLID sur Wikipedia, ou la plupart des architectes informatiques, nous devons nous assurer que chaque classe n'a qu'une seule responsabilité. J'aimerais savoir pourquoi, parce que si tout le monde semble être d'accord avec cette règle, personne ne semble être d'accord sur les raisons de cette règle.
Certains citent une meilleure maintenance, d'autres disent que cela facilite les tests ou rend la classe plus robuste, ou plus sûre. Qu'est-ce qui est correct et qu'est-ce que cela signifie réellement? Pourquoi améliore-t-il la maintenance, facilite les tests ou renforce le code?
design-patterns
architecture
solid
single-responsibility
Bastien Vandamme
la source
la source
Réponses:
Modularité. N'importe quel langage décent vous donnera les moyens de coller des morceaux de code, mais il n'y a pas de moyen général de décoller un gros morceau de code sans que le programmeur effectue une opération à la source. En regroupant de nombreuses tâches dans une construction de code, vous vous privez, ainsi que d'autres personnes, de la possibilité de combiner ses éléments d'une autre manière, et vous introduisez des dépendances inutiles qui pourraient affecter les modifications apportées à un élément.
SRP est tout aussi applicable aux fonctions qu'aux classes, mais les langages POO traditionnels sont relativement peu aptes à coller des fonctions ensemble.
la source
Une meilleure maintenance, des tests faciles, une résolution plus rapide des bogues ne sont que des résultats (très agréables) de l'application de SRP. La principale raison (comme le dit Robert C. Matin) est:
En d'autres termes, SRP soulève la localité du changement .
SRP favorise également le code DRY. Tant que nous avons des classes qui n'ont qu'une seule responsabilité, nous pouvons choisir de les utiliser n'importe où. Si nous avons une classe qui a deux responsabilités, mais que nous n’avons besoin que d’une seule et que la seconde se mêle, nous avons 2 options:
la source
Il est facile de créer du code pour résoudre un problème particulier. Il est plus compliqué de créer du code qui corrige ce problème tout en permettant d’apporter des modifications ultérieures en toute sécurité. SOLID fournit un ensemble de pratiques qui améliorent le code.
Quant à savoir lequel est correct: les trois. Ce sont tous des avantages à utiliser une responsabilité unique et la raison pour laquelle vous devriez l’utiliser.
En ce qui concerne ce qu'ils veulent dire:
Essayez de créer du code pendant un certain temps en suivant le principe et revisitez ce code ultérieurement pour apporter des modifications. Vous verrez l’énorme avantage qu’elle procure.
Vous faites la même chose pour chaque classe et vous vous retrouvez avec plus de classes, toutes conformes à SRP. Cela rend la structure plus complexe du point de vue des connexions, mais la simplicité de chaque classe le justifie.
la source
Voici les arguments qui, à mon avis, soutiennent l'affirmation selon laquelle le principe de la responsabilité unique est une bonne pratique. Je fournis également des liens vers d'autres ouvrages où vous pouvez lire des raisonnements encore plus détaillés - et plus éloquents que le mien:
Meilleure maintenance : idéalement, chaque fois qu'une fonctionnalité du système doit être modifiée, une seule classe doit être modifiée. Une correspondance claire entre les classes et les responsabilités signifie que tout développeur impliqué dans le projet peut identifier de quelle classe il s'agit. (Comme @ MaciejChałapuk l'a noté, voir Robert C. Martin, «Clean Code» .)
Tests plus faciles : idéalement, les classes devraient avoir une interface publique aussi minime que possible et les tests ne devraient concerner que cette interface publique. Si vous ne pouvez pas tester avec clarté, car de nombreuses parties de votre classe sont privées, cela signifie clairement que votre classe a trop de responsabilités et que vous devez la scinder en sous-classes plus petites. Veuillez noter que cela s’applique également aux langues dans lesquelles il n’existe aucun membre de la classe «public» ou «privé»; le fait d'avoir une petite interface publique signifie qu'il est très clair pour le code client quelles parties de la classe elle est censée utiliser. (Voir Kent Beck, "Développement piloté par les tests" pour plus de détails.)
Code robuste : votre code ne faillira pas plus ou moins souvent car il est bien écrit; mais, comme tout code, son but ultime n'est pas de communiquer avec la machine , mais avec d'autres développeurs (voir Kent Beck, "Modèles d'implémentation" , chapitre 1.) Une base de code claire est plus facile à raisonner, donc moins de bugs seront introduits et moins de temps s’écoulera entre la découverte d’un bogue et sa résolution.
la source
Il y a un certain nombre de raisons, mais celle que j'aime bien est l'approche utilisée par plusieurs des premiers programmes UNIX: faites une chose bien. Il est déjà assez difficile de faire cela avec une seule chose, et de plus en plus difficile plus vous essayez de faire.
Une autre raison est de limiter et de contrôler les effets secondaires. J'ai adoré mon ouvre-porte de cafetière combinée. Malheureusement, le café débordait généralement lorsque j'avais des visiteurs. J'ai oublié de fermer la porte après avoir préparé du café l'autre jour et quelqu'un l'a volé.
Sur le plan psychologique, vous ne pouvez suivre qu'un petit nombre de choses à la fois. Les estimations générales sont sept plus ou moins deux. Si une classe fait plusieurs choses, vous devez les suivre toutes en même temps. Cela réduit votre capacité à suivre ce que vous faites. Si une classe fait trois choses et que vous ne voulez qu’une d’entre elles, vous pouvez épuiser votre capacité de garder une trace de ces choses avant de faire quoi que ce soit avec la classe.
Faire plusieurs choses augmente la complexité du code. Au-delà du code le plus simple, la complexité croissante augmente les risques de bogues. De ce point de vue, vous voulez que les classes soient aussi simples que possible.
Tester une classe qui fait une chose est beaucoup plus simple. Vous n'avez pas à vérifier que la deuxième chose que la classe a faite a eu lieu ou n'a pas eu lieu pour chaque test. Vous n'avez pas non plus à résoudre les problèmes et à refaire le test lorsqu'un de ces tests échoue.
la source
Parce que le logiciel est organique. Les exigences changent constamment, vous devez donc manipuler des composants avec le moins de maux de tête possible. En ne suivant pas les principes de SOLID, vous risquez de vous retrouver avec une base de code concrète.
Imaginez une maison avec un mur de béton porteur. Que se passe-t-il lorsque vous supprimez ce mur sans aucun support? La maison va probablement s'effondrer. Nous ne le souhaitons pas dans les logiciels. Nous structurons donc les applications de manière à ce que vous puissiez facilement déplacer / remplacer / modifier des composants sans causer beaucoup de dommages.
la source
Je suis la pensée:
1 Class = 1 Job
.Utilisation d'une analogie physiologique: moteur (système neural), respiration (poumons), digestif (estomac), olfactif (observation), etc. Chacun de ces contrôleurs comportera un sous-ensemble de contrôleurs, mais chacun n'a qu'une responsabilité, que ce soit de gérer le fonctionnement de chacun de leurs sous-systèmes respectifs ou s’il s’agit d’un sous-système de point de terminaison ne réalisant qu’une tâche, telle que lever le doigt ou faire pousser un follicule pileux.
Ne confondez pas le fait qu'il peut s'agir d'un gestionnaire plutôt que d'un travailleur. Certains travailleurs finissent par être promus au poste de responsable, lorsque le travail qu'ils effectuent est devenu trop compliqué pour qu'un processus puisse le gérer seul.
La partie la plus compliquée de mon expérience est de savoir quand désigner une classe en tant que processus Opérateur, Superviseur ou Manager. Quoi qu'il en soit, vous devrez observer et indiquer ses fonctionnalités pour une responsabilité (opérateur, superviseur ou gestionnaire).
Lorsqu'un classe / objet exécute plus d'un de ces rôles, vous constaterez que le processus global commencera à avoir des problèmes de performances ou à traiter des goulots d'étranglement.
la source
Lung
classe, je dirais qu'une instance dePerson
still doit être immobileBreathe
, et qu'ellePerson
doit donc contenir suffisamment de logique pour au moins déléguer les responsabilités associées à cette méthode. De plus, je suggérerais qu'une forêt d'entités interconnectées qui ne sont accessibles que par un propriétaire commun est souvent plus facile à raisonner qu'une forêt comprenant plusieurs points d'accès indépendants.Person
classe a pour tâche de déléguer toutes les fonctionnalités associées à une entité monstrueusement trop compliquée d '"être humain réel", y compris l'association de ses différentes parties . Avoir une entité qui fait 500 choses est moche, mais si c'est ainsi que fonctionne le système réel modélisé, il peut être préférable d'avoir une classe qui délègue 500 fonctions tout en conservant une identité, plutôt que de tout faire avec des pièces disjointes.Person
séparer chaque processus de la classe de base tout en rendant compte de la réussite ou de l'échec de la chaîne, sans toutefois affecter l'autre. 500 fonctions (bien qu'elles soient définies à titre d'exemple, seraient excessives et impossibles à prendre en charge) J'essaie de garder ce type de fonctionnalité dérivée et modulaire.Fred.vision.lookAt(George)
cela a du sens, mais permettre au code desomeEyes = Fred.vision; someTubes = Fred.digestion
paraître dégueulasse, car il occulte la relation entresomeEyes
etsomeTubes
.Surtout avec un principe aussi important que la responsabilité unique, je m'attendrais personnellement à ce que les gens adoptent ce principe pour de nombreuses raisons.
Certaines de ces raisons pourraient être:
Notez également que SRP est livré avec un ensemble de principes SOLID. Respecter le PÉR et ignorer le reste est aussi grave que de ne pas le faire au départ. Vous ne devez donc pas évaluer SRP en tant que tel, mais dans le contexte de tous les principes SOLID.
la source
... test is not affected by other responsibilities the class has
, pourriez-vous s'il vous plaît élaborer sur celui-ci?La meilleure façon de comprendre l’importance de ces principes est d’en avoir besoin.
Quand j'étais programmeur débutant, je ne pensais pas beaucoup au design. En fait, je ne savais même pas que les modèles de design existaient. Au fur et à mesure que mes programmes grandissaient, changer une chose signifiait changer beaucoup d'autres choses. Il était difficile de localiser les bogues, le code était énorme et répétitif. Il n'y avait pas beaucoup de hiérarchie d'objets, les choses étaient partout. Ajouter quelque chose de nouveau ou supprimer quelque chose de vieux entraînerait des erreurs dans les autres parties du programme. Allez comprendre.
Ce n'est peut-être pas grave pour les petits projets, mais dans les grands projets, les choses peuvent être très cauchemardesques. Plus tard, quand je suis tombé sur les concepts de modèles de design, je me suis dit: "ah oui, faire ça aurait rendu les choses tellement plus faciles alors".
Vous ne pouvez vraiment pas comprendre l'importance des modèles de conception jusqu'à ce que le besoin s'en fait sentir. Je respecte les modèles car, d’expérience, je peux affirmer qu’ils facilitent la maintenance du code et le rendent robuste.
Cependant, tout comme vous, je ne suis toujours pas sûr des "tests faciles", car je n'ai pas encore eu besoin de passer aux tests unitaires.
la source
La réponse est, comme d’autres l’ont déjà fait remarquer, qu’elles sont toutes correctes et qu’elles se nourrissent les unes des autres. Faciliter les tests facilite la maintenance, rend le code plus robuste, facilite la maintenance, etc.
Tout cela revient à un principe clé - le code doit être aussi petit et faire le moins possible pour que le travail soit effectué. Cela s'applique à une application, à un fichier ou à une classe, tout autant qu'à une fonction. Plus un code est volumineux, plus il est difficile à comprendre, à maintenir, à étendre ou à tester.
Je pense que cela peut se résumer en un mot: portée. Portez une attention particulière à la portée des artefacts: moins il y a d'éléments à portée d'une application, mieux c'est.
Portée étendue = plus de complexité = plus de moyens pour que les choses tournent mal.
la source
Si une classe est trop grande, il devient difficile de la maintenir, de la tester et de la comprendre, d’autres réponses ont couvert cette volonté.
Il est possible qu'une classe ait plus d'une responsabilité sans problème, mais vous rencontrez rapidement des problèmes avec des classes trop complexes.
Cependant, le simple fait de définir «une seule responsabilité» facilite le fait de savoir quand vous avez besoin d'une nouvelle classe .
Cependant, définir «responsabilité» est difficile, cela ne signifie pas «faire tout ce que la spécification de l'application dit», la véritable compétence consiste à savoir comment décomposer le problème en petites unités de «responsabilité».
la source