web-dev-qa-db-fra.com

Comment déterminer si une classe répond au principe de responsabilité unique?

Le principe de responsabilité unique est basé sur le principe de cohésion élevé. La différence entre les deux est qu'une classe très cohérente comporte un ensemble de responsabilités fortement liées, tandis que les cours adhérant à SRP n'ont qu'une seule responsabilité.

Mais comment déterminons-nous si une classe particulière présente un ensemble de responsabilités et est donc très cohérente, ou si elle n'a qu'une seule responsabilité et adhère ainsi à SRP? En d'autres termes, n'est-ce pas plus ou moins subjectif, puisqu'il peut envisager une classe très granuleuse (et, à ce titre, la classe adhère à SRP), tandis que d'autres peuvent considérer que cela n'est pas assez granulaire?

34
user1483278

Pourquoi oui, c'est très subjectif, et c'est le sujet de nombreux programmeurs de débats chauffés à face rouge.

Il n'y a pas vraiment de réponse et la réponse peut changer lorsque votre logiciel devient plus complexe. Quelle était une fois qu'une seule tâche bien définie peut éventuellement devenir plusieurs tâches mal définies. C'est toujours le frottement aussi. Comment choisissez-vous la bonne façon de diviser un programme en tâches?

À propos du seul conseil que je puisse donner est celui-ci: utilisez votre meilleur jugement (et votre collègue "). Et rappelez-vous que les erreurs peuvent être corrigées (généralement) si vous les attrapez assez rapidement.

21
Jason Baker

Bob Martin (oncle Bob), originaire de [~ # ~ # ~] [~ # ~] Principes de qui SRP Le premier, dit à ce sujet (je suis paraphrasant, je ne peux pas rappeler les mots actuels):

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

S'il a plus d'une raison, il ne respecte pas SRP.

13
Oded

Je peux vous donner plusieurs règles de pouce.

  • Quelle est la facilité de nommer la classe? Si une classe est difficile à nommer, cela fait probablement trop.
  • Combien de méthodes publiques la classe a-t-elle? 7 +/- 2 est une bonne règle de base. Si la classe en a plus que cela, vous devriez penser à la diviser en plusieurs classes.
  • Y a-t-il des groupes cohérents de méthodes publiques utilisées dans des contextes séparés?
  • Combien de méthodes privées ou de membres de données sont là? Si la classe a une structure interne complexe, vous devez probablement le refroidir afin que les internes soient emballés dans des classes plus petites séparées.
  • 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 supérieure à quelques centaines de lignes de long, vous devriez probablement le scinder.
7
Dima

Les principes de responsabilité unique indiquent que chaque module logiciel ne devrait avoir qu'une seule raison de changer. Sur un article récent oncle Bob a expliqué "raison de changer",

Cependant, comme vous le pensez de ce principe, rappelez-vous que les raisons du changement sont des personnes. Ce sont des personnes qui demandent des changements. Et vous ne voulez pas confondre ces personnes, ni vous, en mélangeant ensemble le code que de nombreuses personnes différentes se soucient de différentes raisons.

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

6
theD

Pour répondre à cela, prenez un pas en arrière et envisagez le intention du principe de responsabilité unique. Pourquoi est-ce un principe de conception recommandé en premier lieu?

Le principe est de "compartimenter" la base de code, de sorte que le code relatif à une seule "responsabilité" est isolé dans une seule unité. Cela facilite la recherche et la comprendre le code, et plus important encore, cela signifie que les modifications apportées à la "responsabilité" n'auront qu'une seule unité de code.

Ce que vous ne voulez absolument jamais dans un système, c'est quand une petite chance provoque une autre partie apparemment indépendante du code échouer ou modifier le comportement. Le SRP aide à isoler des bugs et des changements.

Alors, quelle est la "responsabilité" alors? C'est quelque chose qui pourrait éventuellement changer de manière indépendante d'autres changements. Dites que vous avez un programme qui peut enregistrer certains paramètres dans un fichier de configuration XML et les lire dans le fichier. Est-ce une responsabilité unique, ou est "chargée" et "sauvegarder" deux responsabilités différentes? Tout type de changement au format ou à la structure du fichier nécessiterait de modifier à la fois la charge et la sauvegarde de la logique. C'est donc une responsabilité unique qui devrait être représentée par une seule classe. Envisagez maintenant une application qui peut exporter certaines données dans le format CVS, Excel et XML. Dans ce cas, il est facile d'imaginer qu'un format pourrait changer sans impact sur l'autre. Si vous décidez de modifier le délimiteur au format CVS, il ne doit pas affecter la sortie Excel. Donc, dans ce cas, il devrait être représenté par trois classes distinctes (partage présumé la même interface).

4
JacquesB

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

Nous savons que les classes doivent être clairement et facilement définies. Mais, afin de définir une telle classe, nous devons avoir une notion claire de la manière dont une classe convient à la conception globale. Sans les exigences de type cascade qui, paradoxalement, sont considérées comme une anti-motif ... cela est difficile à atteindre.

Nous pouvons mettre en œuvre une conception de classe avec une architecture qui fonctionne dans la plupart des cas, comme MVC. Dans les applications MVC, nous supposons que des données, une interface utilisateur et une exigence pour les deux de communiquer.

Avec une architecture de base, il est plus facile d'identifier les cas où les règles de responsabilité unique sont cassées. PAR EXEMPLE. Transmettre une instance d'un contrôle utilisateur à un modal.

2
P.Brian.Mackey

Une manière concrète peut être faite, sur la base de ce que vous avez dit - cette haute cohésion conduit une seule responsabilité unique, vous pouvez mesurer la cohésion. Une classe de cohésion maximale contient tous les domaines utilisés dans toutes les méthodes. Bien qu'une classe cohérente maximale n'est pas toujours possible ni souhaitable, il est toujours préférable d'atteindre ce sujet. Avoir cet objectif de conception de classe, il est assez facile de déduire que votre classe ne peut pas avoir de nombreuses méthodes ou champs (certains disent au plus 7).

Une autre solution provient des bases purs de OOP - modèle après des objets réels. Il est beaucoup plus facile de voir la responsabilité des objets réels. Cependant, si le véritable objet est trop complexe la casser dans plusieurs Des objets contautifs chacun ont sa propre responsabilité.

1
m3th0dman

Juste pour la discussion, je vais élever une classe de [~ # ~] Juce [~ # ~ ~] appelé Audiosamplebuffer . Maintenant, cette classe existe pour tenir 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 déterminé à un flotteur IEEE 32 bits plutôt que d'avoir une représentation numérique variable ou des mots (mais ce n'est pas un problème avec moi). Il existe des fonctions membres qui vous permettent d'obtenir les numrannels ou les numsemples et les pointeurs sur un canal particulier. Vous pouvez faire un audiosampluffer plus long ou plus court. Je présume l'ancien zéro-pads le tampon pendant que ce dernier tronque.

Il existe quelques membres privés de cette classe utilisée pour allouer de l'espace dans le tas spécial que Juce utilise.

Mais c'est ce que l'audiosampluffer manque (et j'ai eu plusieurs discussions avec Jules à ce sujet): un membre appelé SampleRate. Comment pourrait manquer cela?

La responsabilité unique selon laquelle un audiosampluffer doit être rempli consiste à représenter de manière adéquate le son physique que l'on entend que ses échantillons représentent. Lorsque vous entrez un audiopluffer à partir de quelque chose qui lit un fichier sonore ou à partir d'un flux, il existe un paramètre supplémentaire que vous devez l'obtenir et le transmettre avec l'audiosamplebuffer pour traiter des méthodes (disons que c'est un filtre) qui doit connaître le taux d'échantillonnage ou, Finalement, à une méthode qui joue Le tampon doit être entendu (ou le diffuser à un autre endroit). Peu importe.

Mais ce que vous avez à faire est de continuer à transmettre ce samplate, ce qui est inhérent à l'audio spécifique vivant dans l'audiosampluffer, autour de partout. J'ai vu le code où un constante 44100.0f a été transmis à une fonction, car le programmeur ne semblait pas savoir quoi faire d'autre.

C'est un exemple de défaut de répondre à sa responsabilité unique.

1