Mein drittes Wissenshäppchen hat das Single Responsibility
Principle zum Thema. TL;DR Das Single-Responsibility-Principle
(SRP) ist das erste SOLID-Prinzip: Eine Klasse soll genau eine
Aufgabe und damit einen Grund haben, geändert zu werden. Ich
übertrage diesen Gedanken auch auf Methoden und andere Komponenten,
damit du ihren Code leichter verstehen und getrennt testen kannst.
Dabei brauchst du einen passenden Ausgleich zwischen hoher Kohäsion
und geringer Kopplung, weil viele kleine Komponenten zusätzliche
Abhängigkeiten erzeugen und das Gesamtsystem schwerer überschaubar
machen können. Kapitelmarken [00:00:26] SRP als erstes
SOLID-Prinzip [00:01:37] Kohäsion: Zusammenhang von Methoden und
Attributen [00:02:45] Fehler durch mehrere Aufgaben in einer Klasse
[00:03:37] SRP für Variablen und Methoden [00:04:33]
Datenbankzugriff und E-Mail-Versand getrennt testen [00:06:30] SRP
auf Architekturebene und bei Microservices [00:08:01] Mehrere
Aufgaben an Namen erkennen [00:08:48] Allgemeine Klassennamen und
Repository-Methoden [00:10:31] Architekturschichten und
Wiederverwendung [00:11:59] Legacy-Software und automatisierte
Tests [00:13:42] Die Gott-Klasse als Anti-Pattern [00:15:00]
Verständlicher Code und isolierte Tests [00:17:35] Überblick über
viele kleine Komponenten behalten [00:20:06] Zusätzliche
Abhängigkeiten durch kleine Klassen [00:22:11] Hohe Kohäsion und
geringe Kopplung abwägen [00:23:12] Fachbegriffe: Cohesion und
Coupling [00:23:50] Kleine Komponenten früh voneinander trennen
[00:25:42] Weiterführende Links und Feedback Inhalt Das SRP ist das
erste der sogenannten SOLID-Prinzipien. Robert "Uncle Bob" Martin
definiert es so: There should never be more than one reason for a
class to change. Jede Klasse sollte genau einen einzigen Grund
haben, um geändert zu werden. Man kann das Prinzip aber auch auf
anderen Ebenen (z.B. Variablen, Methoden oder ganze Komponenten)
berücksichtigen. Erklärung Nur Dinge, die zusammengehören, werden
auch zusammen abgebildet. Jedes Ding (Variable, Methode, Klasse,
Komponente) darf nur eine Aufgabe haben. Die Kohäsion der Dinge
sollte hoch sein. Verletzung ist erkennbar an Wörtern wie und oder
oder. Unklare Namen wie z.B. Manager oder Verwaltung sind auch
häufig ein Anzeichen für die Verletzung des Prinzips. Wenn viele
Abhängigkeiten und Dinge, die nichts miteinander zu tun haben, in
einer Klasse zusammengefasst werden, kann beim Anpassen einer
(Teil-)Implementierung eventuell eine damit gar nicht in
Zusammenhang stehende Implementierung negativ beeinträchtigt
werden. Praxisbeispiel: Klasse, die Daten aus der Datenbank liest
und diese direkt in der Oberfläche anzeigt. Klasse hat nicht nur
eine Aufgabe, sondern drei verschiedene: Geschäftslogik,
Datenzugriff, Anzeige. Ein Test der Logik ohne Datenbank und/oder
Oberfläche ist nun unmöglich. Die Wiederverwendbarkeit der Klasse
leidet auch, da die Logik nun nicht mehr ohne Datenbank und/oder
Oberfläche aufgerufen werden kann. Das ist oft ein großes Problem
in Legacy-Anwendungen. Ein krasses Gegenbeispiel/Antipattern zum
SRP wäre eine Gott-Klasse, die alles Mögliche macht. Vorteile Die
Klassen, Methoden und Variablen sind insgesamt kürzer und besser
verständlich. Entwickler können Anpassungen durchführen, ohne dabei
Fehler an anderer Stelle einzubauen. Komponenten sind einfacher
testbar. Das Test-Setup ist kleiner, weil unnötige Abhängigkeiten
nicht bereitgestellt werden müssen. Probleme bei der Testbarkeit
und Verständlichkeit entstehen häufig, wenn zu viel auf einmal
passiert. Zu wenig ist meistens kein Problem. Nachteile Durch viele
kleine Methoden und Klassen leidet evtl. die Übersicht über das
Gesamtsystem und man findet sich schwieriger zurecht. Dem kann man
aber entgegenwirken und es ist erstmal nicht schlimm, viele kleine
Klassen anzulegen, anstatt eine große. Dateien kosten nichts.
Dateien/Klassen können in IDEs mit Shortcuts schnell
gefunden/geöffnet werden. Wenn auf eine vernünftige (einheitliche)
Benennung geachtet wird, sind Klassen auch so gut auffindbar.
Weitere Konventionen (z.B. die Einordnung in Package-Strukturen)
helfen auch bei der Organisation. Wenn Klassen nur eine einzige
Aufgabe haben dürfen, steigt automatisch die nötige Kopplung zu
anderen Klassen, weil deren Funktionalität benötigt wird, um die
Gesamtaufgabe zu erfüllen. Hier gilt es, einen guten Mittelweg zu
finden, der eine möglichst hohe Kohäsion bei gleichzeitg geringer
Kopplung ermöglicht. Literaturempfehlungen Wie zu fast jedem
Wissenshäppchen kann ich jeder/m Anwendungsentwickler/in nur
wärmstens Clean Code* von Uncle Bob ans Herz legen. Das SRP wird
hier auch erklärt und gleich praktisch angewendet. * Links
Permalink zu dieser Podcast-Episode RSS-Feed des Podcasts SRP: The
Single Responsibility Principle ArticleS.UncleBob.PrinciplesOfOod
Single-Responsibility-Prinzip Zusammenfassung Eine Aufgabe pro
Komponente Robert C. Martin, auch als Uncle Bob und Autor von Clean
Code bekannt, beschreibt das Single-Responsibility-Principle (SRP)
über den Grund, eine Klasse zu ändern: Für jede Klasse soll es
genau einen solchen Grund geben. Wenn du ihre Aufgabe anpasst,
änderst du den dazugehörigen Code. Ich übertrage dieses erste
SOLID-Prinzip auch auf Variablen und Methoden sowie auf ganze
Programme. Jede dieser Einheiten soll eine klar abgegrenzte Aufgabe
erfüllen. Wie gut gehört der Code zusammen? Wenn du prüfst, welche
Attribute die Methoden einer Klasse verwenden, bekommst du Hinweise
darauf, wie eng ihre Bestandteile zusammengehören. Der Fachbegriff
dafür ist Kohäsion, auf Englisch Cohesion. Über Softwaremetriken
lässt sich dieser Zusammenhang messen. Greifen verschiedene
Methoden jeweils auf völlig getrennte Attribute zu, lohnt sich ein
genauerer Blick auf die Aufgaben der Klasse. Darin können mehrere
Verantwortlichkeiten stecken. Wenn ich in einer solchen Klasse ein
Attribut ändere, kann das eine weitere Funktion beeinflussen, die
ebenfalls darauf zugreift. Solche Fehler erschweren es dir, den
Code zu pflegen und ihre Ursache nachzuvollziehen. Beispiel: Daten
speichern und eine E-Mail verschicken Bei einer Registrierung
gehören das Speichern eines neuen Kontos und das Verschicken einer
Bestätigungs-E-Mail zum selben fachlichen Ablauf. Technisch sind es
eigenständige Aufgaben. Ich verteile sie auf getrennte Methoden.
Die Methode für den Datenbankzugriff kann Ergebnisse liefern, die
anschließend als Parameter an die Methode für den E-Mail-Versand
gehen. Eine übergeordnete Komponente verbindet die Schritte.
Dadurch kannst du den Datenbankzugriff separat testen. Enthält eine
Methode beide Aufgaben, hängt ihr Test zusätzlich vom
E-Mail-Versand ab. Ein fehlender Mailserver kann dann einen Test
scheitern lassen, der das Speichern prüfen soll. Auch eine
boolesche Variable kann mehrere Aussagen bündeln, etwa "Person ist
über 18 und bekommt einen Nachlass". Später lassen sich die
einzelnen Prüfergebnisse daraus schwer wieder gewinnen. Wenn du die
Bedingungen an dieser Stelle immer gemeinsam brauchst, kann ein
gemeinsamer Wert sinnvoll sein. Hinweise auf mehrere
Verantwortlichkeiten Beim Lesen von Namen und Tests kannst du nach
folgenden Anzeichen suchen: "Und" oder "oder" im Namen: Eine
Methode wie "Speichere Konto und verschicke Bestätigungs-E-Mail"
beschreibt mehrere Aufgaben. Allgemeine Klassennamen: Begriffe wie
"Manager" oder "Verwaltung" können darauf hinweisen, dass die
eigentliche fachliche Aufgabe noch unklar ist. Hier fehlt häufig
eine passende Abstraktion, etwa eine getrennte Klasse zum Lesen
oder Schreiben von Objekten. Aufwendige Tests: Wenn du für eine
kleine Funktion viele Abhängigkeiten bereitstellen musst, solltest
du die Verantwortlichkeiten der Klasse prüfen. Besonders
verschachtelte Mocks und Stubs können ein Hinweis sein. Bei einem
Repository sollten die CRUD-Aktionen mindestens eigene Methoden
bekommen. Eine einzige Methode, die über einen Aktionsparameter und
einen großen Switch entscheidet, was sie tun soll, übernimmt
mehrere Aufgaben. Bei einer strengen Auslegung des SRP lassen sich
Lesen und Schreiben auch auf eigene Klassen verteilen. Architektur
und Wiederverwendung Wenn Geschäftslogik fest mit einer
Weboberfläche verbunden ist, erschwert das ihren Einsatz in einer
Konsolenanwendung. Eine fest eingebaute Datenbankabhängigkeit
bindet sie außerdem an diese Datenquelle. Aus meinem Alltag mit
Legacy-Software kenne ich Komponenten, die innerhalb einer
Datenbankschleife Geschäftslogik ausführen und Ergebnisse direkt
auf einer Oberfläche anzeigen. Um die Logik zu testen, brauchst du
dann die Datenbank samt passenden Daten. Wartet die Oberfläche auf
Eingaben, muss zusätzlich eine Person eingreifen, was
automatisierte Tests behindert. Die Gott-Klasse ist ein
Anti-Pattern, bei dem nahezu das gesamte Programm in einer Klasse
steckt. Ein Fehler kann große Teile des Programms beeinträchtigen.
Einzelne Funktionen lassen sich wegen der vielen verbundenen
Aufgaben schwer isoliert prüfen. Auf Architekturebene lässt sich
derselbe Gedanke bei Microservices wiederfinden: Ein Service bildet
eine abgegrenzte Fachlichkeit ab. Änderungen innerhalb dieses
Bereichs bleiben dadurch zunächst auf die betreffende Komponente
begrenzt. Gemeinsam zugängliche Datenfelder können dagegen dazu
führen, dass weitere Komponenten Werte verändern, die fachlich zu
einem anderen Bereich gehören. Was kleine Komponenten erleichtern
Wenn eine Komponente eine klar umrissene Aufgabe erfüllt, enthält
sie meist weniger Code. Du kannst sie schneller verstehen und die
passende Stelle für eine Änderung leichter finden. Getrennte
Verantwortlichkeiten erleichtern außerdem, vorhandenen Code erneut
einzusetzen. Für mich spielt besonders das isolierte Testen eine
Rolle. Externe Abhängigkeiten lassen sich dabei durch Mocks oder
Stubs ersetzen. Je kleiner der Bereich ist, den du prüfst, desto
weniger musst du für den Test vorbereiten. Überblick und
Abhängigkeiten Durch viele kleine Klassen kann es schwieriger
werden,...
Kommentare (0)
Melde dich an, um einen Kommentar zu schreiben.