Comment déterminer si une classe respecte le principe de responsabilité unique?

34

Le principe de responsabilité unique repose sur le principe de haute cohésion. La différence entre les deux réside dans le fait qu’une classe très cohérente comporte un ensemble de responsabilités étroitement liées, tandis que les classes adhérant au PRS n’ont qu’une responsabilité.

Mais comment déterminer si une classe particulière comporte un ensemble de responsabilités et est donc simplement très cohérente, ou si elle n’a qu’une responsabilité et adhère donc au PRS? En d'autres termes, n'est-il pas plus ou moins subjectif, puisque certains peuvent considérer une classe comme très granulaire (et en tant que telle croiront que la classe adhère au PRS), tandis que d'autres peuvent le considérer comme n'étant pas assez granulaire?

utilisateur1483278
la source

Réponses:

21

Pourquoi oui, c’est très subjectif, et c’est le sujet de nombreux débats houleux et virulents dans lesquels les programmeurs se lancent.

Il n'y a pas vraiment de réponse unique, et la réponse peut changer à mesure que votre logiciel devient plus complexe. Ce qui était autrefois une tâche unique bien définie peut éventuellement devenir plusieurs tâches mal définies. C'est toujours le problème aussi. Comment choisissez-vous la bonne façon de diviser un programme en tâches?

Le seul conseil que je puisse vous donner est le suivant: utilisez votre meilleur jugement (et celui de vos collègues). Et rappelez-vous que les erreurs peuvent (généralement) être corrigées si vous les corrigez assez tôt.

Jason Baker
la source
J'aimerais que l'informatique ressemble davantage à la science actuelle. La subjectivité n'a pas sa place dans la science réelle. Les principes SOLID conviennent parfaitement, mais il est nécessaire de les réitérer pour minimiser la subjectivité et maximiser l’objectivité. Ce n'est pas encore arrivé, ce qui me fait douter de leur légitimité dans le monde réel.
DarkNeuron
13

Bob Martin (Oncle Bob), à l'origine des principes SOLID dont SRP est le premier, dit à ce propos (je me paraphrase, je ne me souviens plus des mots):

Une classe ne devrait avoir qu'une seule raison de changer

S'il a plus d'une raison, il n'adhère pas à SRP.

Oded
la source
14
Cela ne fait que répéter la définition, mais adhérer au srp reste assez subjectif.
Andy
7

Je peux vous donner plusieurs règles de base.

  • Est-il facile de nommer la classe? Si une classe est difficile à nommer, elle en fait probablement trop.
  • Combien de méthodes publiques la classe a-t-elle? 7 +/- 2 est une bonne règle. Si la classe a plus que cela, vous devriez penser à la diviser en plusieurs classes.
  • Existe-t-il des groupes cohérents de méthodes publiques utilisées dans des contextes distincts?
  • Combien de méthodes privées ou de membres de données existe-t-il? Si la classe a une structure interne complexe, vous devriez probablement la refactoriser afin que les internes soient regroupés dans des classes plus petites distinctes.
  • Et la règle la plus simple: quelle est la taille de la classe? Si vous avez un fichier d'en-tête C ++ contenant une seule classe de plusieurs centaines de lignes, vous devriez probablement le séparer.
Dima
la source
2
En ce qui concerne votre second point, voir uxmyths.com/post/931925744/…
Cameron Martin
7
Pas du tout d'accord sur 7 +/- 2 - le principe de responsabilité unique concerne la cohésion sémantique et non les nombres arbitraires.
JacquesB
1
La règle empirique n’a pas besoin de preuves scientifiques indépendantes. La méthode scientifique moderne est centenaire, l'architecture et l'ingénierie millénaire. La règle générale pour les méthodes publiques est "plusieurs" et aucun paramètre n'est "quelques-uns". Dans d'autres nouvelles, même si les dessins de certains enfants montrent autrement les bras des gens ne leur sortent pas de la tête [citation nécessaire].
Abuzittin gillifirca
@CameronMartin Selon votre configuration, l'interface d'une classe peut être ou ne pas être facilement disponible pour vous. Rechercher une interface utilisateur est à peine équivalent à écrire du code - si je dois consulter la documentation toutes les minutes, je suis au moins le double du temps nécessaire pour effectuer un travail réel.
Clairière
6

Selon les principes de responsabilité unique, chaque module logiciel ne doit avoir qu'une seule raison de changer. Dans un article récent, oncle Bob a expliqué "raison de changer",

Cependant, lorsque vous réfléchissez à ce principe, rappelez-vous que les raisons du changement sont les personnes. Ce sont les gens qui demandent des changements. Et vous ne voulez pas confondre ces personnes, ni vous-même, en mélangeant le code qui intéresse beaucoup de personnes différentes pour des raisons différentes.

Il a ensuite expliqué le concept avec un exemple ICI .

leD
la source
C'est un excellent article, écrit par l'homme lui-même.
MrDustpan
4

Pour répondre à cette question, prenez du recul et considérez l’ intention du principe de responsabilité unique. Pourquoi est-ce un principe de conception recommandé en premier lieu?

Le but du principe est de "compartimenter" la base de code, de sorte que le code relatif à une "responsabilité" unique soit isolé dans une seule unité. Cela facilite la recherche et la compréhension du code et, plus important encore, cela signifie que les modifications apportées à la "responsabilité" n'auront une incidence que sur une seule unité de code.

Ce que vous ne voulez absolument jamais dans un système, c’est quand une petite chance fait échouer une autre partie du code apparemment non liée ou modifier son comportement. Le SRP aide à isoler les bugs et les changements.

Alors qu'est-ce une "responsabilité" alors? C'est quelque chose qui pourrait éventuellement changer indépendamment des autres changements. Supposons que vous ayez un programme capable de sauvegarder certains paramètres dans un fichier de configuration XML et de les relire à partir du fichier. Est-ce une responsabilité unique ou est-ce que "charger" et "sauver" sont deux responsabilités différentes? Toute modification du format ou de la structure du fichier nécessiterait de modifier la logique de chargement et de sauvegarde. C'est donc une responsabilité unique qui devrait être représentée par une classe unique. Considérons maintenant une application qui peut exporter des données au format CVS, Excel et XML. Dans ce cas, il est facile d'imaginer qu'un format puisse changer sans affecter l'autre. Si vous décidez de modifier le délimiteur au format CVS, cela ne devrait pas affecter la sortie Excel.

JacquesB
la source
2

OO dit que les classes sont un groupement de données et une fonctionnalité. Cette définition laisse beaucoup de place à une interprétation subjective.

Nous savons que les classes doivent être définies clairement et facilement. Mais, pour définir une telle classe, nous devons avoir une idée claire de la façon dont une classe s’intègre dans la conception globale. Sans exigences de type cascade qui, paradoxalement, sont considérées comme des anti-modèles ... cela est difficile à réaliser.

Nous pouvons implémenter une conception de classe avec une architecture qui fonctionne dans la plupart des cas, comme MVC. Dans les applications MVC, nous supposons que nous n’avons que des données, une interface utilisateur et une obligation pour les deux personnes de communiquer.

Avec une architecture de base, il est plus facile d'identifier les cas où des règles de responsabilité unique sont enfreintes. EG Passage d'une instance d'un contrôle utilisateur à un modal.

P.Brian.Mackey
la source
1

Juste pour les besoins de la discussion, je vais aborder un cours de JUCE appelé AudioSampleBuffer . Maintenant, cette classe existe pour contenir un extrait (ou peut-être un extrait assez long) d'audio. Il connaît le nombre de canaux, le nombre d'échantillons (par canal), semble être affecté à la méthode float IEEE 32 bits plutôt que d'avoir une représentation numérique variable ou une taille de texte (mais ce n'est pas un problème pour moi). Il existe des fonctions membres qui vous permettent d’obtenir numChannels ou numSamples et des pointeurs sur un canal particulier. Vous pouvez créer un AudioSampleBuffer plus long ou plus court. Je présume que les premiers tamponnent le tampon pendant que les derniers tronquent.

Quelques membres privés de cette classe sont utilisés pour l'allocation d'espace dans le tas spécial utilisé par JUCE.

Mais c’est ce qui manque à AudioSampleBuffer (et j’ai eu plusieurs discussions à ce sujet avec Jules): un membre a appelé SampleRate. Comment cela pourrait-il manquer?

La seule responsabilité qu'un AudioSampleBuffer doit remplir est de représenter correctement le son physique que l'on entend que ses échantillons représentent. Lorsque vous entrez un AudioSampleBuffer à partir de quelque chose qui lit un fichier son ou un flux, vous devez obtenir un paramètre supplémentaire et le transmettre avec les méthodes de traitement AudioSampleBuffer aux méthodes de traitement (disons qu'il s'agit d'un filtre) qui doit connaître le taux d'échantillonnage ou, finalement, à une méthode qui lit la mémoire tampon pour la faire entendre (ou la diffuse vers un autre endroit). Peu importe.

Mais ce que vous devez faire est de continuer à transmettre ce SampleRate, inhérent à l’audio spécifique de AudioSampleBuffer, partout. J'ai vu du code dans lequel une constante 44100.0f était transmise à une fonction, car le programmeur ne semblait pas savoir quoi faire d'autre.

C'est un exemple d'échec à assumer sa seule responsabilité.

robert bristow-johnson
la source
1

Une solution concrète peut être élaborée à partir de ce que vous avez dit: une cohésion élevée est une responsabilité qui permet de mesurer la cohésion. Une classe de cohésion maximale contient tous les champs utilisés dans toutes les méthodes. Bien qu'une classe de cohésion maximale ne soit pas toujours possible ni souhaitable, il est toujours préférable de l'atteindre. Avec cet objectif de conception de classe, il est assez facile de déduire que votre classe ne peut pas avoir plusieurs méthodes ou champs (certains disent au plus 7).

Une autre méthode consiste à utiliser les bases pures de la programmation orientée objet: modéliser des objets réels. C'est beaucoup plus facile de voir la responsabilité des objets réels. Cependant, si l'objet réel est trop complexe, divisez-le en plusieurs objets contenant chacun sa propre responsabilité.

m3th0dman
la source