Le principe de responsabilité unique est défini sur wikipedia comme suit :
Le principe de responsabilité unique est un principe de programmation informatique qui stipule que chaque module, classe ou fonction doit avoir la responsabilité d'une partie des fonctionnalités fournies par le logiciel, responsabilité qui doit être entièrement encapsulée par la classe.
Si une classe ne devrait avoir qu'une seule responsabilité, comment peut-elle avoir plus d'une méthode? Chaque méthode n'aurait-elle pas une responsabilité différente, ce qui voudrait dire que la classe aurait plus d'une responsabilité.
Tous les exemples que j'ai vus démontrant le principe de responsabilité unique utilisent un exemple de classe qui ne comporte qu'une méthode. Il peut être utile de voir un exemple ou d’avoir une explication d’une classe avec plusieurs méthodes qui peuvent toujours être considérées comme ayant une seule responsabilité.
Réponses:
La responsabilité unique peut ne pas être quelque chose qu'une seule fonction peut remplir.
Cette classe peut casser le principe de responsabilité unique. Non pas parce qu'il a deux fonctions, mais si le code doit
getX()
etgetY()
doit satisfaire différentes parties prenantes qui peuvent exiger un changement. Si le vice-président, M. X envoie un mémo indiquant que tous les nombres doivent être exprimés en nombres à virgule flottante et que la directrice de la comptabilité, Mme Y, insiste sur le fait que tous les chiffres examinés par son service doivent rester des nombres entiers, indépendamment de ce que M. X pense bien, cette classe aurait intérêt à avoir une seule idée de qui est responsable, car les choses sont sur le point de devenir confuses.Si SRP avait été suivi, il serait clair si la classe Location contribue aux éléments exposés par M. X et son groupe. Expliquez clairement à quoi la classe est responsable et vous saurez quelle directive aura une incidence sur cette classe. S'ils ont tous les deux un impact sur cette classe, elle a été mal conçue pour minimiser l'impact du changement. "Une classe ne devrait avoir qu'une seule raison de changer" ne signifie pas que la classe entière ne peut faire qu'une seule petite chose. Cela signifie que je ne devrais pas être capable de regarder la classe et de dire que M. X et Mme Y ont un intérêt pour cette classe.
Autre que des choses comme ça. Non, plusieurs méthodes conviennent. Donnez-lui simplement un nom qui précise quelles méthodes appartiennent à la classe et lesquelles ne le sont pas.
Le PÉR d'Oncle Bob concerne plus la loi de Conway que la loi de Curly . Oncle Bob préconise d'appliquer la loi de Curly (faire une chose) à des fonctions et non à des classes. SRP met en garde contre le mélange des raisons de changer ensemble. La loi de Conway stipule que le système suivra la manière dont les informations d'une organisation circulent. Cela conduit à suivre le PÉR parce que vous ne vous souciez pas de ce dont vous n'avez jamais entendu parler.
Les gens continuent de vouloir que SRP traite de toutes les raisons pour limiter la portée. Il y a plus de raisons de limiter la portée que SRP. Je limite encore la portée en insistant pour que la classe soit une abstraction pouvant prendre un nom qui garantisse que regarder à l'intérieur ne vous surprendra pas .
Vous pouvez appliquer la loi de Curly aux cours. Vous êtes en dehors de ce dont parle Oncle Bob, mais vous pouvez le faire. Vous vous trompez lorsque vous commencez à penser que cela signifie une fonction. C'est comme penser qu'une famille ne devrait avoir qu'un seul enfant. Avoir plus d'un enfant ne l'empêche pas d'être une famille.
Si vous appliquez la loi de Curly à une classe, tout dans la classe devrait concerner une seule idée unificatrice. Cette idée peut être large. L'idée pourrait être la persistance. Si certaines fonctions utilitaires de journalisation sont présentes, elles sont clairement déplacées. Peu importe si M. X est le seul à s'intéresser à ce code.
Le principe classique à appliquer ici s'appelle Séparation des préoccupations . Si vous séparez toutes vos préoccupations, on pourrait faire valoir que ce qui reste à un endroit donné est une préoccupation. C'est ce que nous appelions cette idée avant que le film City Slickers de 1991 ne nous présente le personnage Curly.
C'est bon. C'est simplement que ce que l'Oncle Bob appelle une responsabilité n'est pas une préoccupation. Une responsabilité envers lui n'est pas une chose sur laquelle vous vous concentrez. C'est quelque chose qui peut vous forcer à changer. Vous pouvez vous concentrer sur une préoccupation tout en créant un code responsable pour différents groupes de personnes ayant différents programmes.
Peut-être que vous ne vous souciez pas de ça. Bien. Penser que le fait de «faire une chose» résoudra tous vos problèmes de conception montre un manque d'imagination de ce qu'une «chose» peut finir par être. Une autre raison de limiter la portée est l'organisation. Vous pouvez imbriquer plusieurs "une chose" dans d'autres "une chose" jusqu'à ce que vous ayez un tiroir pour ordures plein de tout. J'ai parlé à ce sujet avant
Bien entendu, la raison classique pour limiter la portée de la programmation orientée objet est que la classe contient des champs privés et que, plutôt que d'utiliser des accesseurs pour partager ces données, nous plaçons toutes les méthodes nécessitant ces données dans la classe où elles peuvent utiliser les données en privé. Nombreux sont ceux qui trouvent cela trop restrictif à utiliser comme limiteur de portée, car toutes les méthodes qui appartiennent à la même classe n'utilisent pas exactement les mêmes champs. J'aime m'assurer que toute idée qui a réuni les données soit la même que celle qui a rassemblé les méthodes.
La manière fonctionnelle de regarder ceci est que
a.f(x)
eta.g(x)
sont simplement f a (x) et g a (x). Pas deux fonctions mais un continuum de paires de fonctions qui varient ensemble. Lea
n'a même pas besoin d'y avoir de données. Il pourrait tout simplement savoir comment vous savez quif
et lag
mise en œuvre que vous allez utiliser. Les fonctions qui changent ensemble vont de pair. C'est bon vieux polymorphisme.SRP n'est qu'une des nombreuses raisons pour limiter la portée. C'est un bon. Mais pas le seul.
la source
La clé ici est la portée ou, si vous préférez, la granularité . Une partie de la fonctionnalité représentée par une classe peut être encore divisée en parties de fonctionnalité, chaque partie étant une méthode.
Voici un exemple. Imaginez que vous deviez créer un fichier CSV à partir d'une séquence. Si vous souhaitez vous conformer à la norme RFC 4180, l'implémentation de l'algorithme et le traitement de tous les cas extrêmes prendraient un certain temps.
Le faire en une seule méthode donnerait un code qui ne serait pas particulièrement lisible, et surtout, la méthode ferait plusieurs choses à la fois. Par conséquent, vous allez le scinder en plusieurs méthodes. par exemple, l’un d’eux peut être chargé de générer l’en-tête, c’est-à-dire la toute première ligne du fichier CSV, alors qu’une autre méthode convertirait une valeur de tout type en une représentation sous forme de chaîne adaptée au format CSV, tandis qu’une autre déterminerait si la valeur doit être placée entre guillemets.
Ces méthodes ont leur propre responsabilité. La méthode qui vérifie s’il est nécessaire d’ajouter ou non des guillemets a la sienne et la méthode qui génère l’en-tête en a une. Ceci est SRP appliqué aux méthodes .
Désormais, toutes ces méthodes ont un objectif en commun: prendre une séquence et générer le fichier CSV. C'est la seule responsabilité de la classe .
Pablo H a commenté:
En effet. L'exemple CSV que j'ai donné a idéalement une méthode publique et toutes les autres méthodes sont privées. Un meilleur exemple serait une file d'attente, implémentée par une
Queue
classe. Cette classe contiendrait, en gros, deux méthodes:push
(également appeléeenqueue
) etpop
(aussi appeléedequeue
).La responsabilité de
Queue.push
est d'ajouter un objet à la queue de la file d'attente.La responsabilité de
Queue.pop
consiste à retirer un objet de la tête de la file et à gérer le cas où la file est vide.La responsabilité de la
Queue
classe est de fournir une logique de file d'attente.la source
Une fonction est une fonction.
Une responsabilité est une responsabilité.
Un mécanicien a la responsabilité de réparer les voitures, ce qui implique des diagnostics, des tâches de maintenance simples, des travaux de réparation, une délégation de tâches à d'autres, etc.
Une classe de conteneur (liste, tableau, dictionnaire, carte, etc.) a la responsabilité de stocker des objets, ce qui implique de les stocker, de permettre leur insertion, de fournir un accès, une sorte de commande, etc.
Une seule responsabilité ne signifie pas qu'il y a très peu de code / fonctionnalité, cela signifie que quelle que soit la fonctionnalité "appartient ensemble" sous la même responsabilité.
la source
La responsabilité unique ne signifie pas nécessairement qu'il ne fait qu'une chose.
Prenons par exemple une classe de service utilisateur:
Cette classe a plusieurs méthodes mais sa responsabilité est claire. Il fournit un accès aux enregistrements d'utilisateur dans le magasin de données. Ses seules dépendances sont le modèle utilisateur et le magasin de données. Il est faiblement couplé et très cohésif, ce à quoi SRP essaie vraiment de vous faire réfléchir.
SRP ne doit pas être confondu avec le "principe de séparation des interfaces" (voir SOLID ). Selon le principe de séparation des interfaces (ISP), des interfaces plus petites et plus légères sont préférables à des interfaces plus grandes et plus généralisées. Go utilise beaucoup le fournisseur de services Internet dans sa bibliothèque standard:
SRP et ISP sont certes liés, mais l’un n’implique pas l’autre. ISP est au niveau de l'interface et SRP au niveau de la classe. Si une classe implémente plusieurs interfaces simples, elle ne peut plus avoir qu'une seule responsabilité.
Merci à Luaan d'avoir souligné la différence entre FAI et SRP.
la source
UserService
etUser
à être UpperCamelCase, mais les méthodesCreate
,Exists
etUpdate
je l' aurais fait lowerCamelCase.Il y a un chef dans un restaurant. Sa seule responsabilité est de cuisiner. Pourtant, il peut cuisiner des steaks, des pommes de terre, du brocoli et cent autres choses. Souhaitez-vous embaucher un chef par plat sur votre menu? Ou un chef pour chaque composant de chaque plat? Ou un chef qui peut assumer sa seule responsabilité: cuisiner?
Si vous demandez à ce chef de faire la paie également, vous violez le PÉR.
la source
Contre-exemple: stocker l'état mutable.
Supposons que vous ayez la classe la plus simple de tous les temps, dont le seul travail est de stocker un fichier
int
.Si vous étiez limité à une seule méthode, vous pourriez avoir un
setState()
, ou ungetState()
, sauf si vous cassez l'encapsulation et rendezi
public.Il est donc clair que cette responsabilité unique nécessite d’ avoir au moins 2 méthodes sur cette classe. QED.
la source
Vous interprétez mal le principe de responsabilité unique.
La responsabilité unique n'équivaut pas à une seule méthode. Ils signifient différentes choses. En développement logiciel, on parle de cohésion . Les fonctions (méthodes) qui ont une grande cohésion "appartiennent" ensemble et peuvent être considérées comme une seule responsabilité.
Il appartient au développeur de concevoir le système de manière à respecter le principe de responsabilité unique. On peut voir cela comme une technique d'abstraction et est donc parfois une question d'opinion. La mise en œuvre du principe de responsabilité unique facilite principalement le test et la compréhension de son architecture et de sa conception.
la source
Il est souvent utile (dans n’importe quelle langue, mais particulièrement dans les langues OO) de regarder les choses et de les organiser du point de vue des données plutôt que des fonctions.
Ainsi, considérez que la responsabilité d’une classe est de maintenir l’intégrité des données et d’aider à les utiliser correctement. Clairement, cela est plus facile à faire si tout le code est dans une classe, plutôt que sur plusieurs classes. L'ajout de deux points est fait de manière plus fiable et le code est plus facilement maintenu, avec une
Point add(Point p)
méthode dans laPoint
classe que d'avoir cela ailleurs.Et en particulier, la classe ne doit rien exposer qui pourrait entraîner des données incohérentes ou incorrectes. Par exemple, si un
Point
doit se situer dans un plan (0,0) à (127,127), le constructeur et toutes les méthodes qui modifient ou produisent une nouvellePoint
ont la responsabilité de vérifier les valeurs qui leur sont données et de rejeter toute modification qui violerait ce principe. exigence. (Souvent, quelque chose comme unPoint
serait immuable, et s'assurer qu'il n'y avait aucun moyen de modifier unPoint
après sa construction serait alors aussi une responsabilité de la classe)Notez que la superposition ici est parfaitement acceptable. Vous pourriez avoir une
Point
classe pour traiter des points individuels et unePolygon
classe pour traiter un ensemble dePoint
s; ceux-ci ont toujours des responsabilités distinctes, car lesPolygon
délégués assument toute la responsabilité de s'occuper de tout ce qui a un rapport avecPoint
(par exemple, s'assurer qu'un point a à la fois unex
et uney
valeur) pour laPoint
classe.la source