web-dev-qa-db-fra.com

Assurez-vous que chaque classe n'a qu'une seule responsabilité, pourquoi?

Selon la documentation de Microsoft, l'article Wikipedia SOLID principe, ou la plupart des architectes informatiques, nous devons nous assurer que chaque classe n'a qu'une seule responsabilité. Je voudrais savoir pourquoi, parce que si tout le monde semble d'accord avec cela personne ne semble être d’accord sur les raisons de cette règle.

Certains citent une meilleure maintenance, certains disent qu'elle fournit des tests faciles ou rend la classe plus robuste, ou la sécurité. Qu'est-ce qui est correct et qu'est-ce que cela signifie réellement? Pourquoi rend-il la maintenance meilleure, les tests plus faciles ou le code plus robuste?

37
Bastien Vandamme

Modularité. Tout langage décent vous donnera les moyens de coller des morceaux de code, mais il n'y a aucun moyen général de décoller un gros morceau de code sans que le programmeur n'intervienne sur la source. En branchant un grand nombre de tâches dans une seule construction de code, vous vous privez vous-même et les autres de l'opportunité de combiner ses éléments de différentes manières et introduisez des dépendances inutiles qui pourraient entraîner des modifications d'un élément affectant les autres.

SRP est tout aussi applicable aux fonctions qu'aux classes, mais les langages courants OOP sont relativement pauvres pour coller des fonctions ensemble.

57
Doval

Une meilleure maintenance, des tests faciles, une correction plus rapide des bogues ne sont que des résultats (très agréables) de l'application de SRP. La raison principale (comme le dit Robert C. Matin) est:

Une classe doit avoir une et une seule raison de changer.

En d'autres termes, SRP augmente la localité de changement.

SRP promeut également DRY code. Tant que nous avons des classes qui n'ont qu'une seule responsabilité, nous pouvons choisir de les utiliser où nous voulons. Si nous avons une classe qui a deux responsabilités, mais nous avons besoin un seul d'entre eux et le second interfèrent, nous avons 2 options:

  1. Copiez-collez la classe dans une autre et peut-être même créez un autre mutant multi-responsabilité (augmentez un peu la dette technique).
  2. Divisez la classe et faites-la telle qu'elle devrait être en premier lieu, ce qui peut être coûteux en raison de l'utilisation intensive de la classe d'origine.
30
Maciej Chałapuk

Est facile de créer du code pour résoudre un problème particulier. Il est plus compliqué de créer du code qui résout ce problème tout en permettant d'effectuer des modifications ultérieures en toute sécurité. SOLID fournit un ensemble de pratiques qui améliore le code.

Quant à savoir laquelle est correcte: les trois. Ce sont tous des avantages d'utiliser la responsabilité unique et la raison pour laquelle vous devez l'utiliser.

Quant à ce qu'ils signifient:

  • Un meilleur entretien signifie qu'il est plus facile de changer et ne change pas aussi souvent. Parce qu'il y a moins de code et que ce code se concentre sur quelque chose de spécifique, si vous devez changer quelque chose qui n'est pas lié à la classe, la classe n'a pas besoin d'être changée. De plus, lorsque vous devez changer de classe, tant que vous n'avez pas besoin de changer l'interface publique, vous n'avez qu'à vous soucier de cette classe et de rien d'autre.
  • Un test facile signifie moins de tests, moins de configuration. Il n'y a pas autant de pièces mobiles sur la classe, donc le nombre d'échecs possibles sur la classe est plus petit, et donc moins de cas que vous devez tester. Il y aura moins de champs/membres privés à configurer.
  • En raison des deux ci-dessus, vous obtenez une classe qui change moins et échoue moins, et est donc plus robuste.

Essayez de créer du code pendant un certain temps en suivant le principe et revisitez ce code plus tard pour apporter des modifications. Vous verrez l'énorme avantage qu'il offre.

Vous faites de même pour chaque classe et vous vous retrouvez avec plus de classes, toutes conformes à SRP. Il rend la structure plus complexe du point de vue des connexions, mais la simplicité de chaque classe le justifie.

21
Miyamoto Akira

Voici les arguments qui, à mon avis, soutiennent l'affirmation selon laquelle le principe de responsabilité unique est une bonne pratique. Je propose également des liens vers d'autres publications, où vous pouvez lire des raisonnements encore plus détaillés - et plus éloquents que les miens:

  • Meilleure maintenance : idéalement, chaque fois qu'une fonctionnalité du système doit changer, il y aura une et une seule classe à changer. 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 l'a noté @ MaciejChałapuk, voir Robert C. Martin, "Clean Code" .)

  • Tests plus faciles : idéalement, les classes devraient avoir une interface publique aussi minimale que possible, et les tests devraient porter uniquement sur cette interface publique. Si vous ne pouvez pas tester avec clarté, car de nombreuses parties de votre classe sont privées, cela indique clairement que votre classe a trop de responsabilités et que vous devez la diviser en sous-classes plus petites. Veuillez noter que cela s'applique également aux langues où il n'y a pas de membres de classe "public" ou "privé"; avoir une petite interface publique signifie qu'il est très clair pour le code client quelles parties de la classe il est censé utiliser. (Voir Kent Beck, "Test-Driven Development" pour plus de détails.)

  • Code robuste : votre code n'échouera pas plus ou moins souvent car il est bien écrit; mais, comme tout code, son but ultime est de ne pas communiquer avec la machine, mais avec les autres développeurs (voir Kent Beck, "Implementation Patterns" , Chapter 1. ) Une base de code claire est plus facile à raisonner, donc moins de bogues seront introduits et moins de temps passera entre la découverte d'un bogue et sa correction.

4
logc

Il y a plusieurs raisons, mais celle que j'aime est l'approche utilisée par la plupart des premiers programmes UNIX: faites bien une chose. Il est déjà assez difficile de le faire avec une chose, et cela devient d'autant plus difficile que vous essayez de faire plus de choses.

Une autre raison est de limiter et de contrôler les effets secondaires. J'ai adoré mon ouvre-porte combiné. Malheureusement, le café débordait généralement lorsque j'avais des visiteurs. J'ai oublié de fermer la porte après avoir fait du café l'autre jour et quelqu'un l'a volé.

Du point de vue psychologique, vous ne pouvez suivre que quelques éléments à la fois. Les estimations générales sont de 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 n'en voulez qu'une seule, vous pouvez épuiser votre capacité à garder une trace des 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, une complexité croissante augmente la probabilité de bugs. De ce point de vue, vous voulez que les cours 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 ou ne s'est pas produite pour chaque test. Vous n'avez pas non plus à réparer les conditions défectueuses et à retester lorsque l'un de ces tests échoue.

3
BillThor

Parce que le logiciel est organique. Les exigences changent constamment, vous devez donc manipuler les composants avec le moins de maux de tête possible. En ne suivant pas les principes SOLID, vous pouvez vous retrouver avec une base de code définie concrètement.

Imaginez une maison avec un mur de béton porteur. Que se passe-t-il lorsque vous retirez ce mur sans aucun support? La maison va probablement s'effondrer. Dans les logiciels, nous ne voulons pas cela, nous structurons donc les applications de manière à ce que vous puissiez facilement déplacer/remplacer/modifier des composants sans causer beaucoup de dommages.

2
CodeART

Surtout avec un principe aussi important que la responsabilité unique, je m'attends personnellement à ce qu'il y ait de nombreuses raisons pour lesquelles les gens adoptent ce principe.

Certaines de ces raisons peuvent être:

  • Maintenance - SRP garantit que le changement de responsabilité dans une classe n'affecte pas les autres responsabilités, ce qui simplifie la maintenance. En effet, si chaque classe n'a qu'une seule responsabilité, les modifications apportées à une responsabilité sont isolées des autres responsabilités.
  • Test - Si une classe a une responsabilité, il est beaucoup plus facile de comprendre comment tester cette responsabilité. Si une classe a plusieurs responsabilités, vous devez vous assurer que vous testez la bonne et que le test n'est pas affecté par les autres responsabilités de la classe.

Notez également que SRP est livré avec un ensemble de principes SOLID. Respecter SRP et ignorer le reste est tout aussi mauvais que de ne pas le faire en premier lieu. Vous ne devez donc pas évaluer SRP seul, mais dans le contexte de tous les principes SOLID.

1
Euphoric

Je suis la pensée: 1 Class = 1 Job.

Utilisation d'une analogie en physiologie: moteur (système neuronal), respiration (poumons), digestif (estomac), olfactif (observation), etc. Chacun d'eux aura un sous-ensemble de contrôleurs, mais ils n'ont chacun qu'une seule responsabilité, celle de gérer la façon dont chacun de leurs sous-systèmes respectifs fonctionne ou s’il s’agit d’un sous-système de point final et n’exécute qu’une seule tâche, comme lever un doigt ou faire pousser un follicule pileux.

Ne confondez pas le fait qu'il peut agir en tant que gestionnaire au lieu d'un travailleur. Certains travailleurs ont finalement été promus au poste de gestionnaire, lorsque le travail qu'ils effectuaient est devenu trop compliqué pour qu'un seul processus puisse être géré seul.

La partie la plus compliquée de ce que j'ai vécu est de savoir quand désigner une classe comme processus d'opérateur, de superviseur ou de gestionnaire. Quoi qu'il en soit, vous devrez observer et indiquer sa fonctionnalité pour 1 responsabilité (opérateur, superviseur ou gestionnaire).

Lorsqu'une classe/un objet effectue plus de 1 de ces rôles de type, vous constaterez que le processus global commencera à avoir des problèmes de performances, ou traitera des goulots d'étranglement.

1
GoldBishop

La meilleure façon de comprendre l'importance de ces principes est d'en avoir le besoin.

Quand j'étais programmeur débutant, je ne pensais pas beaucoup à la conception, en fait, je ne savais même pas que les modèles de conception existaient. À mesure que mes programmes grandissaient, changer une chose signifiait changer beaucoup d'autres choses. Il était difficile de retrouver 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. L'ajout de quelque chose de nouveau ou la suppression de quelque chose d'ancien entraînerait des erreurs dans les autres parties du programme. Allez comprendre.

Sur les petits projets, cela n'a pas d'importance, mais dans les grands projets, les choses peuvent être très cauchemardesques. Plus tard, lorsque je suis tombé sur les concepts de design patters, je me suis dit: "oh yah, cela 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 fasse sentir. Je respecte les modèles car, par expérience, je peux dire qu'ils rendent la maintenance du code facile et robuste.

Cependant, tout comme vous, je ne suis toujours pas certain des "tests faciles", car je n'ai pas encore eu besoin de me lancer dans les tests unitaires.

1
harsimranb

La réponse est, comme d'autres l'ont souligné, toutes sont correctes, et elles se nourrissent toutes les unes les autres, plus faciles à tester rend la maintenance plus facile rend le code plus robuste rend la maintenance plus facile et ainsi de suite ...

Tout cela se résume à un principal clé - le code doit être aussi petit et faire aussi peu que nécessaire pour faire le travail. Cela s'applique à une application, un fichier ou une classe tout autant qu'à une fonction. Plus un code fait de choses, plus il est difficile à comprendre, à maintenir, à étendre ou à tester.

Je pense que cela peut se résumer en un seul mot: portée. Portez une attention particulière à la portée des artefacts, moins il y a de choses à portée à un point particulier d'une application, mieux c'est.

Portée élargie = plus de complexité = plus de façons pour que les choses tournent mal.

1
jmoreno

Si une classe est trop grande, il devient difficile à maintenir, à tester et à comprendre, d'autres réponses ont couvert cette volonté.

Il est possible pour une classe d'avoir plus d'une responsabilité sans problèmes, mais vous rencontrez rapidement des problèmes avec des classes trop complexes.

Cependant, avoir une règle simple de "une seule responsabilité" rend juste plus facile à savoir quand vous avez besoin d'une nouvelle classe.

Cependant, définir la "responsabilité" est difficile, cela ne signifie pas "faire tout ce que dit la spécification de l'application", la vraie compétence est de savoir comment décomposer le problème en petites unités de "responsabilité".

1
Ian