Laut Microsoft-Dokumentation, dem Wikipedia SOLID-Hauptartikel oder den meisten IT-Architekten müssen wir sicherstellen, dass jede Klasse nur eine Verantwortung hat. Ich würde gerne wissen warum, denn wenn alle mit dieser Regel einverstanden zu sein scheinen, scheint sich niemand über die Gründe dieser Regel einig zu sein.
Einige geben eine bessere Wartung an, andere sagen, dass dies einfache Tests ermöglicht oder die Klasse robuster oder sicherer macht. Was ist richtig und was bedeutet es eigentlich? Warum macht es die Wartung besser, das Testen einfacher oder den Code robuster?
design-patterns
architecture
solid
single-responsibility
Bastien Vandamme
quelle
quelle
Antworten:
Modularität. Jede anständige Sprache gibt Ihnen die Möglichkeit, Teile des Codes zusammenzufügen, aber es gibt keine allgemeine Möglichkeit, einen großen Teil des Codes zu lösen, ohne dass der Programmierer eine Operation an der Quelle vornimmt. Indem Sie viele Aufgaben in einem Codekonstrukt zusammenfassen, nehmen Sie sich und anderen die Möglichkeit, die Teile auf andere Weise zu kombinieren, und führen unnötige Abhängigkeiten ein, die dazu führen können, dass sich Änderungen an einem Teil auf die anderen auswirken.
SRP ist für Funktionen genauso anwendbar wie für Klassen, aber die gängigen OOP-Sprachen können Funktionen nur relativ schlecht zusammenfügen.
quelle
Bessere Wartung, einfache Tests und schnellere Fehlerbehebung sind nur (sehr angenehme) Ergebnisse der Anwendung von SRP. Der Hauptgrund (wie Robert C. Matin es ausdrückt) ist:
Mit anderen Worten, SRP erhöht die Änderungslokalität .
SRP fördert auch DRY-Code. Solange wir Klassen haben, die nur eine Verantwortung haben, können wir sie verwenden, wo immer wir wollen. Wenn wir eine Klasse haben, die zwei Verantwortlichkeiten hat, aber nur eine von ihnen benötigt und die zweite stört, haben wir zwei Möglichkeiten:
quelle
Ist einfach, Code zu erstellen, um ein bestimmtes Problem zu beheben. Es ist komplizierter, Code zu erstellen, der das Problem behebt, während spätere Änderungen sicher vorgenommen werden können. SOLID bietet eine Reihe von Methoden, die den Code verbessern.
Welches ist richtig? Alle drei. Sie alle haben den Vorteil, dass Sie nur eine Verantwortung tragen und den Grund, warum Sie sie verwenden sollten.
Was sie bedeuten:
Versuchen Sie, Code für eine Weile nach dem Prinzip zu erstellen, und wiederholen Sie diesen Code später, um einige Änderungen vorzunehmen. Sie werden den enormen Vorteil sehen, den es bietet.
Sie tun dasselbe für jede Klasse und erhalten am Ende mehr Klassen, die alle der SRP entsprechen. Dies macht die Struktur unter dem Gesichtspunkt der Verbindungen komplexer, aber die Einfachheit jeder Klasse rechtfertigt dies.
quelle
Die folgenden Argumente stützen meines Erachtens die Behauptung, dass das Prinzip der einfachen Verantwortung eine gute Praxis ist. Ich biete auch Links zu weiterer Literatur, in der Sie noch detailliertere Begründungen lesen können - und die beredter sind als meine:
Bessere Wartung : Im Idealfall muss immer dann, wenn sich eine Funktionalität des Systems ändern muss, nur eine Klasse geändert werden. Eine klare Zuordnung zwischen Klassen und Verantwortlichkeiten bedeutet, dass jeder am Projekt beteiligte Entwickler identifizieren kann, um welche Klasse es sich handelt. (Wie @ MaciejChałapuk bemerkt hat, siehe Robert C. Martin, "Clean Code" .)
Einfacheres Testen : Im Idealfall sollten Klassen eine möglichst minimale öffentliche Schnittstelle haben, und Tests sollten nur diese öffentliche Schnittstelle behandeln. Wenn Sie nicht klar testen können, weil viele Teile Ihrer Klasse privat sind, ist dies ein klares Zeichen dafür, dass Ihre Klasse zu viele Verantwortlichkeiten hat und dass Sie sie in kleinere Unterklassen aufteilen sollten. Bitte beachten Sie, dass dies auch für Sprachen gilt, in denen es keine "öffentlichen" oder "privaten" Klassenmitglieder gibt. Eine kleine öffentliche Schnittstelle bedeutet, dass für den Client-Code sehr klar ist, welche Teile der Klasse verwendet werden sollen. ( Weitere Informationen finden Sie in Kent Beck, "Test-Driven Development" .)
Robuster Code : Ihr Code schlägt nicht mehr oder weniger häufig fehl, weil er gut geschrieben ist. Wie bei jedem Code ist es jedoch das ultimative Ziel, nicht mit der Maschine , sondern mit anderen Entwicklern zu kommunizieren (siehe Kent Beck, "Implementierungsmuster" , Kapitel 1.). Eine eindeutige Codebasis lässt sich leichter überlegen, sodass weniger Fehler auftreten und es vergeht weniger Zeit zwischen dem Erkennen und Beheben eines Fehlers.
quelle
Es gibt eine Reihe von Gründen, aber der eine, den ich mag, ist der Ansatz, den viele der frühen UNIX-Programme verwenden: Mach eine Sache gut. Es ist schon schwer genug, das mit einer Sache zu tun, und es wird immer schwieriger, je mehr Sie versuchen, es zu tun.
Ein weiterer Grund ist die Begrenzung und Kontrolle von Nebenwirkungen. Ich liebte meine Kombination Kaffeemaschine Türöffner. Leider lief der Kaffee normalerweise über, wenn ich Besucher hatte. Ich habe vergessen, die Tür zu schließen, nachdem ich neulich Kaffee gemacht hatte und jemand hat ihn gestohlen.
Aus psychologischer Sicht können Sie immer nur wenige Dinge auf einmal im Auge behalten. Allgemeine Schätzungen sind sieben plus oder minus zwei. Wenn eine Klasse mehrere Aufgaben ausführt, müssen Sie alle gleichzeitig im Auge behalten. Dadurch können Sie nicht mehr nachverfolgen, was Sie gerade tun. Wenn eine Klasse drei Aufgaben ausführt und Sie nur eine davon ausführen möchten, können Sie möglicherweise die Übersicht verlieren, bevor Sie tatsächlich etwas mit der Klasse anfangen.
Das Durchführen mehrerer Aufgaben erhöht die Codekomplexität. Über den einfachsten Code hinaus erhöht eine zunehmende Komplexität die Wahrscheinlichkeit von Fehlern. Von diesem Standpunkt aus möchten Sie, dass Klassen so einfach wie möglich sind.
Das Testen einer Klasse, die eines tut, ist viel einfacher. Sie müssen nicht bei jedem Test überprüfen, ob das Zweite, was die Klasse tut oder nicht, passiert ist. Sie müssen die fehlerhaften Bedingungen auch nicht beheben und erneut testen, wenn einer dieser Tests fehlschlägt.
quelle
Weil Software organisch ist. Die Anforderungen ändern sich ständig, sodass Sie Komponenten mit möglichst wenig Kopfschmerzen bearbeiten müssen. Wenn Sie nicht den SOLID-Grundsätzen folgen, erhalten Sie möglicherweise eine konkrete Codebasis.
Stellen Sie sich ein Haus mit einer tragenden Betonwand vor. Was passiert, wenn Sie diese Mauer ohne Unterstützung entfernen? Das Haus wird wahrscheinlich zusammenbrechen. In Software wollen wir das nicht, deshalb strukturieren wir Anwendungen so, dass Sie Komponenten leicht verschieben, austauschen oder modifizieren können, ohne viel Schaden zu verursachen.
quelle
Ich folge dem Gedanken:
1 Class = 1 Job
.Unter Verwendung einer Physiologie-Analogie: Motor (neuronales System), Atmung (Lunge), Verdauung (Magen), Riechstoff (Beobachtung) usw. Jede von diesen hat eine Untergruppe von Kontrollpersonen, aber jede hat nur eine Verantwortung, ob sie zu verwalten ist die Art und Weise, wie die jeweiligen Subsysteme funktionieren, oder ob sie ein Endpunkt-Subsystem sind und nur eine Aufgabe ausführen, z. B. einen Finger heben oder einen Haarfollikel wachsen lassen.
Verwechseln Sie nicht die Tatsache, dass es sich um einen Manager handelt und nicht um einen Arbeiter. Einige Mitarbeiter werden schließlich zum Manager befördert, wenn die von ihnen ausgeführte Arbeit zu kompliziert geworden ist, als dass ein Prozess für sich allein ausreichen würde.
Der komplizierteste Teil meiner Erfahrungen besteht darin, zu wissen, wann eine Klasse als Operator, Supervisor oder Manager-Prozess zu bestimmen ist. Unabhängig davon müssen Sie die Funktionen für 1 Verantwortung (Bediener, Vorgesetzter oder Manager) beobachten und kennzeichnen.
Wenn eine Klasse / ein Objekt mehr als eine dieser Rollen ausführt, treten im gesamten Prozess Leistungsprobleme oder Prozessengpässe auf.
quelle
Lung
Klasse sein sollte, würde ich davon ausgehen, dass eine Instanz vonPerson
dennochBreathe
und somit diePerson
Klasse genügend Logik enthalten sollte, um zumindest die mit dieser Methode verbundenen Verantwortlichkeiten zu delegieren. Ich würde außerdem vorschlagen, dass eine Gesamtstruktur von miteinander verbundenen Entitäten, auf die nur ein gemeinsamer Eigentümer zugreifen kann, häufig einfacher zu beurteilen ist als eine Gesamtstruktur mit mehreren unabhängigen Zugriffspunkten.Person
Klasse die Delegation aller Funktionen hat, die mit einer ungeheuer überkomplizierten "realen menschlichen Wesen" -Entität verbunden sind, einschließlich der Assoziation ihrer verschiedenen Teile . Von einer Entität 500 Dinge tun zu lassen ist hässlich, aber wenn das zu modellierende reale System so funktioniert, ist es möglicherweise besser, eine Klasse zu haben, die 500 Funktionen delegiert und dabei eine Identität beibehält, als alles mit disjunkten Teilen zu tun.Person
trennen und trotzdem über Erfolg / Misserfolg berichten, aber nicht unbedingt den anderen beeinflussen. 500 Funktionen (obwohl als Beispiel gesetzt, wäre überbordend und nicht unterstützbar) Ich versuche, diese Art von Funktionalität abgeleitet und modular zu halten.Fred.vision.lookAt(George)
sinnvoll ist, aber so dass Code zu sagensomeEyes = Fred.vision; someTubes = Fred.digestion
scheinen eklig , da sie die Beziehung zwischen verschleiernsomeEyes
undsomeTubes
.Gerade bei so wichtigen Prinzipien wie der Einzelverantwortung würde ich persönlich erwarten, dass es viele Gründe gibt, warum Menschen dieses Prinzip anwenden.
Einige dieser Gründe könnten sein:
Beachten Sie auch, dass SRP mit einem Paket von SOLID-Prinzipien geliefert wird. Sich an SRP zu halten und den Rest zu ignorieren, ist genauso schlimm wie SRP überhaupt nicht zu tun. Sie sollten SRP also nicht alleine bewerten, sondern im Kontext aller SOLID-Prinzipien.
quelle
... test is not affected by other responsibilities the class has
Könnten Sie dies bitte näher erläutern?Der beste Weg, um die Wichtigkeit dieser Prinzipien zu verstehen, ist, die Notwendigkeit zu haben.
Als ich ein Anfänger in der Programmierung war, habe ich nicht viel über Design nachgedacht. Tatsächlich wusste ich nicht einmal, dass Designmuster existieren. Als meine Programme wuchsen, bedeutete das Ändern einer Sache das Ändern vieler anderer Dinge. Es war schwer, Fehler aufzuspüren, der Code war riesig und wiederholte sich. Es gab nicht viel von einer Objekthierarchie, die Dinge waren überall. Wenn Sie etwas Neues hinzufügen oder etwas Altes entfernen, treten in den anderen Programmteilen Fehler auf. Stelle dir das vor.
Bei kleinen Projekten spielt es vielleicht keine Rolle, aber bei großen Projekten können die Dinge sehr albtraumhaft sein. Als ich später auf die Konzepte von Designmustern stieß, sagte ich mir: "Oh ja, das hätte die Sache dann viel einfacher gemacht."
Sie können die Bedeutung von Entwurfsmustern erst dann wirklich verstehen, wenn die Notwendigkeit entsteht. Ich respektiere die Muster, weil ich aus Erfahrung sagen kann, dass sie die Code-Pflege einfach und den Code robust machen.
Wie Sie bin ich mir jedoch immer noch nicht sicher, ob es sich um ein "einfaches Testen" handelt, da ich noch nicht in das Testen von Einheiten einsteigen musste.
quelle
Die Antwort ist, wie andere betont haben, dass sie alle richtig sind und sich gegenseitig beeinflussen. Einfacher zu testen macht die Wartung einfacher macht den Code robuster macht die Wartung einfacher und so weiter ...
All dies läuft darauf hinaus, dass ein wichtiger Prinzipalcode so klein und so wenig wie nötig ist, um die Arbeit zu erledigen. Dies gilt für eine Anwendung, eine Datei oder eine Klasse genauso wie für eine Funktion. Je mehr Dinge ein Code tut, desto schwieriger ist es zu verstehen, zu warten, zu erweitern oder zu testen.
Ich denke, es kann in einem Wort zusammengefasst werden: Umfang. Achten Sie genau auf den Umfang der Artefakte. Je weniger Dinge an einem bestimmten Punkt in einer Anwendung im Umfang sind, desto besser.
Erweiterter Geltungsbereich = mehr Komplexität = mehr Möglichkeiten, Fehler zu beheben.
quelle
Wenn eine Klasse zu groß ist, ist es schwierig, sie zu pflegen, zu testen und zu verstehen. Andere Antworten haben diesen Willen abgedeckt.
Es ist möglich, dass eine Klasse mehr als eine Verantwortung ohne Probleme hat, aber Sie stoßen bei zu komplexen Klassen schnell auf Probleme.
Eine einfache Regel von „nur einer Verantwortung“ macht es jedoch einfacher zu wissen, wann Sie eine neue Klasse benötigen .
Die Definition von „Verantwortung“ ist jedoch schwierig. Dies bedeutet nicht, dass Sie „alles tun, was in der Anwendungsspezifikation angegeben ist“. Die eigentliche Fertigkeit besteht darin, das Problem in kleine Einheiten von „Verantwortung“ aufzuteilen.
quelle