Wie kann festgestellt werden, ob eine Klasse das Prinzip der Einzelverantwortung erfüllt?

34

Das Prinzip der einheitlichen Verantwortung basiert auf dem Prinzip des hohen Zusammenhalts. Der Unterschied zwischen den beiden besteht darin, dass eine sehr zusammenhängende Klasse eine Reihe von Verantwortlichkeiten aufweist, die eng miteinander verbunden sind, während Klassen, die sich an die SRP halten, nur eine Verantwortung haben.

Aber wie bestimmen wir, ob eine bestimmte Klasse eine Reihe von Verantwortlichkeiten aufweist und daher nur einen hohen Zusammenhalt aufweist oder ob sie nur eine Verantwortung hat und sich somit an die SRP hält? Mit anderen Worten, ist es nicht mehr oder weniger subjektiv, da einige eine Klasse für sehr granular halten (und als solche glauben, dass die Klasse an SRP festhält), während andere sie möglicherweise für nicht granular genug halten?

user1483278
quelle

Antworten:

21

Warum ja, es ist sehr subjektiv und es ist das Thema vieler hitziger, rotgesichtiger Debatten, in die Programmierer geraten.

Es gibt nicht wirklich eine Antwort, und die Antwort kann sich ändern, wenn Ihre Software komplexer wird. Was einmal eine einzelne genau definierte Aufgabe war, kann schließlich zu mehreren schlecht definierten Aufgaben werden. Das ist auch immer der Knackpunkt. Wie wählt man den richtigen Weg, um ein Programm in Aufgaben aufzuteilen?

Der einzige Rat, den ich geben kann, ist: Verwenden Sie Ihr (und das Ihrer Kollegen) bestes Urteilsvermögen. Und denken Sie daran, dass Fehler (normalerweise) korrigiert werden können, wenn Sie sie früh genug bemerken.

Jason Baker
quelle
Ich wünschte, die Informatik wäre mehr wie die eigentliche Wissenschaft. Subjektivität hat in der realen Wissenschaft keinen Platz. Obwohl die SOLID-Prinzipien für sich genommen in Ordnung sind, müssen sie wiederholt werden, um die Subjektivität zu minimieren und die Objektivität zu maximieren. Das ist noch nicht geschehen, weshalb ich ihre Legitimität in der realen Welt in Frage stelle.
DarkNeuron
13

Bob Martin (Onkel Bob), der die SOLID- Prinzipien hervorgebracht hat, von denen SRP die erste ist, sagt darüber (ich kann mich umschreiben, kann mich nicht an die tatsächlichen Wörter erinnern):

Eine Klasse sollte nur einen Grund haben, sich zu ändern

Wenn es mehrere Gründe gibt, wird SRP nicht eingehalten.

Oded
quelle
14
Das ist nur eine Wiederholung der Definition, aber die Einhaltung von srp ist immer noch ziemlich subjektiv.
Andy
7

Ich kann Ihnen einige Faustregeln geben.

  • Wie einfach ist es, die Klasse zu benennen? Wenn es schwierig ist, eine Klasse zu benennen, tut sie wahrscheinlich zu viel.
  • Wie viele öffentliche Methoden hat die Klasse? 7 +/- 2 ist eine gute Faustregel. Wenn die Klasse mehr als das enthält, sollten Sie darüber nachdenken, sie in mehrere Klassen aufzuteilen.
  • Gibt es zusammenhängende Gruppen öffentlicher Methoden, die in getrennten Kontexten verwendet werden?
  • Wie viele private Methoden oder Datenelemente gibt es? Wenn die Klasse eine komplexe interne Struktur hat, sollten Sie sie wahrscheinlich so umgestalten, dass die Interna in separate kleinere Klassen gepackt werden.
  • Und die einfachste Faustregel: Wie groß ist die Klasse? Wenn Sie eine C ++ - Headerdatei haben, die eine einzelne Klasse mit mehr als ein paar hundert Zeilen enthält, sollten Sie diese wahrscheinlich aufteilen.
Dima
quelle
2
Bezüglich Ihres zweiten Punktes siehe uxmyths.com/post/931925744/…
Cameron Martin
7
Über 7 +/- 2 stark streiten - das Prinzip der Einzelverantwortung handelt von semantischem Zusammenhalt, nicht von willkürlichen Zahlen.
JacquesB
1
Eine Faustregel benötigt keinen unabhängigen wissenschaftlichen Nachweis. Moderne wissenschaftliche Methode ist Jahrhunderte alt, Architektur und Ingenieurwesen sind Jahrtausende alt. Die Faustregel für öffentliche Methoden lautet "mehrere", und keiner der Parameter lautet "einige". In anderen Nachrichten, obwohl einige Kinderzeichnungen zeigen, dass die Arme der Menschen nicht aus ihren Köpfen kommen [Zitat erforderlich].
abuzittin gillifirca
@CameronMartin Abhängig von Ihrem Setup ist die Schnittstelle für eine Klasse möglicherweise nicht verfügbar. Das Durchsuchen einer Benutzeroberfläche ist kaum dasselbe wie das Schreiben von Code. Wenn ich die Dokumentation jede Minute einsehen muss, verdoppele ich zumindest die Zeit, die für die eigentliche Arbeit erforderlich ist.
Klarer
6

Nach den Grundsätzen der Einzelverantwortung sollte jedes Softwaremodul nur einen Änderungsgrund haben. In einem kürzlich erschienenen Artikel erklärte Onkel Bob "Grund zur Veränderung",

Denken Sie jedoch beim Nachdenken über dieses Prinzip daran, dass die Gründe für Veränderungen Menschen sind. Es sind Menschen, die Veränderungen fordern. Und Sie möchten diese Personen oder sich selbst nicht verwirren, indem Sie den Code zusammenmischen, den viele unterschiedliche Personen aus unterschiedlichen Gründen interessieren.

Er erläuterte das Konzept anhand eines Beispiels HIER .

theD
quelle
Das ist ein großartiger Artikel, den der Mann selbst geschrieben hat.
MrDustpan
4

Um dies zu beantworten, treten Sie einen Schritt zurück und denken Sie über die Absicht des Grundsatzes der einheitlichen Verantwortung nach. Warum ist es überhaupt ein empfohlenes Konstruktionsprinzip?

Der Zweck des Prinzips besteht darin, die Codebasis zu "unterteilen", so dass Code, der sich auf eine einzelne "Verantwortung" bezieht, in einer einzelnen Einheit isoliert ist. Dies erleichtert das Auffinden und Verstehen des Codes, und was noch wichtiger ist, Änderungen an der "Verantwortung" wirken sich nur auf eine einzige Codeeinheit aus.

Was Sie auf keinen Fall in einem System wollen, ist, wenn eine kleine Chance dazu führt, dass ein anderer scheinbar nicht verwandter Teil des Codes versagt oder das Verhalten ändert. Die SRP hilft, Fehler und Änderungen zu isolieren.

Also, was ist es dann eine "Verantwortung"? Es ist etwas, das sich möglicherweise unabhängig von anderen Veränderungen ändern könnte. Angenommen, Sie haben ein Programm, mit dem Sie einige Einstellungen in einer XML-Konfigurationsdatei speichern und aus der Datei wieder einlesen können. Handelt es sich um eine einzelne Verantwortung, oder handelt es sich beim "Laden" und "Speichern" um zwei unterschiedliche Verantwortlichkeiten? Bei jeder Änderung des Dateiformats oder der Dateistruktur müssen sowohl die Lade- als auch die Speicherlogik geändert werden. Es ist daher eine einzelne Verantwortung, die von einer einzelnen Klasse vertreten werden sollte. Betrachten Sie nun eine App, die einige Daten im CVS-, Excel- und XML-Format exportieren kann. In diesem Fall ist es leicht vorstellbar, dass sich ein Format ändern könnte, ohne dass sich dies auf das andere auswirkt. Wenn Sie das Trennzeichen im CVS-Format ändern, sollte dies keine Auswirkungen auf die Excel-Ausgabe haben.

JacquesB
quelle
2

OO sagt, dass Klassen eine Gruppierung von Daten und Funktionen sind. Diese Definition lässt viel Raum für subjektive Interpretationen.

Wir wissen, dass Klassen klar und einfach definiert werden sollten. Um eine solche Klasse zu definieren, müssen wir jedoch eine klare Vorstellung davon haben, wie eine Klasse in das Gesamtdesign passt. Ohne Anforderungen an den Wasserfalltyp, die paradoxerweise als Antimuster angesehen werden, ist dies schwer zu erreichen.

Wir können ein Klassendesign mit einer Architektur wie MVC implementieren, die in den meisten Fällen funktioniert. In MVC-Anwendungen wird nur vorausgesetzt, dass Daten, eine Benutzeroberfläche und eine Kommunikationsanforderung für beide vorliegen.

Mit einer Basisarchitektur ist es einfacher, Fälle zu identifizieren, in denen Regeln für einzelne Verantwortlichkeiten verletzt werden. EG Übergeben einer Instanz eines Benutzersteuerelements an ein Modal.

P. Brian Mackey
quelle
1

Nur zum Zwecke der Diskussion werde ich eine Klasse von JUCE mit dem Namen AudioSampleBuffer aufrufen . Nun existiert diese Klasse, um ein Snippet (oder vielleicht ein ziemlich langes Snippet) von Audio aufzunehmen. Es ist bekannt, dass die Anzahl der Kanäle und die Anzahl der Samples (pro Kanal) offenbar auf 32-Bit-IEEE-Float festgelegt sind, anstatt eine variable numerische Darstellung oder Wortgröße zu haben (aber das ist für mich kein Problem). Es gibt Member-Funktionen, mit denen Sie die numChannels oder numSamples und Zeiger auf einen bestimmten Kanal abrufen können. Sie können einen AudioSampleBuffer länger oder kürzer machen. Ich gehe davon aus, dass die ersteren den Puffer mit Nullen auffüllen, während die letzteren abschneiden.

Es gibt einige private Member dieser Klasse, die zum Zuweisen von Speicherplatz in dem von JUCE verwendeten speziellen Heap verwendet werden.

Aber das ist, was AudioSampleBuffer fehlt (und ich habe mehrere Diskussionen mit Jules darüber geführt): Ein Mitglied hat angerufen SampleRate. Wie konnte es daran fehlen?

Die einzige Verantwortung, die ein AudioSampleBuffer erfüllen muss, besteht darin, das physische Audio, das man hört, das seine Samples darstellen, angemessen darzustellen. Wenn Sie einen AudioSampleBuffer aus etwas eingeben, das eine Sounddatei oder einen Stream liest, müssen Sie einen zusätzlichen Parameter abrufen und zusammen mit dem AudioSampleBuffer an Verarbeitungsmethoden übergeben (z. B. einen Filter), die die Samplerate kennen müssen, oder schließlich zu einer Methode, die den Puffer abspielt, um gehört zu werden (oder ihn an einen anderen Ort zu streamen). Wie auch immer.

Sie müssen diese SampleRate, die dem spezifischen Audio im AudioSampleBuffer eigen ist, jedoch weiterhin an alle Stellen weitergeben. Ich habe Code gesehen, in dem eine Konstante 44100.0f an eine Funktion übergeben wurde, weil der Programmierer anscheinend nicht wusste, was er sonst tun sollte.

Dies ist ein Beispiel für die Nichterfüllung seiner alleinigen Verantwortung.

Robert Bristow-Johnson
quelle
1

Auf der Grundlage Ihrer Aussagen kann ein konkreter Weg beschritten werden: Ein hoher Zusammenhalt führt zu einer Verantwortung, an der Sie den Zusammenhalt messen können. Eine maximale zusammenhängende Klasse enthält alle Felder, die in allen Methoden verwendet werden. Während eine maximale Kohäsionsklasse nicht immer möglich oder wünschenswert ist, ist es immer noch am besten, diese zu erreichen. Mit diesem Klassenentwurfsziel lässt sich ziemlich leicht ableiten, dass Ihre Klasse nicht viele Methoden oder Felder haben kann (einige sagen höchstens 7).

Ein anderer Weg ist von den Grundlagen des OOP - Modells nach realen Objekten. Es ist viel einfacher, die Verantwortung der realen Objekte zu erkennen. Wenn das reale Objekt jedoch zu komplex ist, zerlegen Sie es in mehrere zusammenhängende Objekte, von denen jedes seine eigene Verantwortung trägt.

m3th0dman
quelle