Die sieben Grundprinzipien der OOP [1]
Das Single Responsibility Principle (SRP) besagt, dass jedes Modul einer Software (in Java also die Klassen und Methoden) genau eine Verantwortung übernehmen soll.
Es gilt auch der Umkehrschluss: Jede Verantwortung soll nur durch ein Modul realisiert werden.

Schlechtes und gutes Beispiel zum SRP, nach Martin 1997 [4]
Autor: Ulrich Helmich 2025, Lizenz: Public domain
Dieses Bild zeigt links ein schlechtes Beispiel, das sich nicht an das Prinzip der alleinigen Verantwortung hält. Eine Klasse Rectangle stellt zwei Methoden zur Verfügung, nämlich draw() und area(). Die Methode draw() stellt das Rechteck graphisch dar, während area() den Flächeninhalt berechnet und als double-Wert zurückgibt.
Die draw()-Methode wird von einer Graphik-Anwendung aufgerufen, die area()-Methode von einer Mathematik-Anwendung.
Wenn sich nun beispielsweise die Anforderungen der Mathematik-Anwendung ändern, kann es sein, dass die Klasse Rectangle angepasst werden muss. Durch unsachgemäße Änderungen kann sich das aber ungewollt auch auf die Graphik-Anwendung auswirken, die dann nicht mehr fehlerfrei funktioniert.
Im Bild rechts sehen wir eine bessere Variante. Es existieren zwei Klassen für Rechtecke, nämlich Rectangle, die für die graphische Darstellung verantwortlich ist und MathRectangle, die für mathematische Berechnungen verantwortlich ist. Sollten sich nun die Anforderungen der Mathe-Anwendung ändern, so muss nur die Klasse MathRectangle angepasst werden. Auf die Graphik-Anwendung hat das keine Auswirkungen mehr.
Konkretes Beispiel
Ein int-Array mit monatlichen Umsatzzahlen soll untersucht werden, in welchen Monaten der Umsatz höher war als im jeweiligen Vormonat.
Dazu wird eine Methode analysiere() geschrieben, die in einer for-Schleife zunächst alle Monate durchläuft (angefangen mit Index i = 1) und mit den jeweiligen Vormonaten vergleicht. Wenn ein Monat i einen höheren Umsatz hat als der Monat i-1, dann wird der Index i in einem zweiten Array gespeichert. Dieser zweite Array enthält also nur die Indices der Monate mit höherem Umsatz als im Vormonat.
Nun kann es sein, dass der Umsatz nur in drei oder vier Monaten höher war als im jeweiligen Vormonat. Es kann aber auch gut sein, dass diese Bedingung für 20 oder sogar für alle Monate zutrifft. Die Länge des zweiten Arrays steht daher zu Beginn der Methode noch gar nicht fest.
Um diese Länge zu bestimmen, wird vor der eigentlichen Analyse-Schleife noch eine Hilfsschleife in die Methode eingebaut, die nur herausfindet, wie viele Monate einen höheren Umsatz haben. Dann wird mit
int[] ergebnis = new int[zaehler];
der Hilfsarray ergebnis initialisiert. Jetzt erst kann die eigentliche Analyse-Schleife starten, welche die Indices der betreffenden Monate in dem Hilfsarray speichert.
In einem ersten Ansatz hat der Entwickler diese Methode mit zwei Vergleichen versehen:
int zaehler = 0; // erste Schleife mit erstem Vergleich // dient zur Bestimmung der Laenge des Hilfsarrays for (int i=1; i < umsatz.length; i++) if (umsatz[i] > umsatz[i-1]) zaehler++; // Erstellen des Hilfsarrays mit korrekter Laenge int[] ergebnis = new int[zaehler]; int j = 0; // Zweite Schleife mit zweitem Vergleich // dient zur Speicherung der Monate mit groesserem Umsatz for (int i=1; i < umsatz.length; i++) if (umsatz[i] > umsatz[i-1]) ergebnis[j++] = i;
Nun ändern sich die Anforderungen an diesen Algorithmus. Es sollen nicht einfach nur die Monate ausgegeben werden, bei denen der Umsatz höher war als im Vormonat, sondern es sollen nur die Monate erfasst werden, bei denen der Umsatz um mindestens 10% höher war als im Vormonat.
Der obige Quelltext muss jetzt an zwei Stellen verändert werden, nämlich bei den beiden Vergleichen. Das ist erstens überflüssige Schreibarbeit und kann zweitens zu Fehlern führen, wenn man nicht beide Bedingungen auf die gleiche Weise verändert.
Wie sähe nun eine bessere Lösung aus, die sich an dieses Prinzip der einzigen Verantwortung hält?
Ganz einfach: Man schreibt eine private Hilfsmethode, in deren Verantwortung dieser Vergleich liegt. Der Quelltext der Analyse-Methode vereinfacht sich dann zu:
int zaehler = 0; // erste Schleife mit erstem Vergleich // dient zur Bestimmung der Laenge des Hilfsarrays for (int i=1; i < umsatz.length; i++) if (groesser(i)) zaehler++; // Erstellen des Hilfsarrays mit korrekter Laenge int[] ergebnis = new int[zaehler]; int j = 0; // Zweite Schleife mit zweitem Vergleich // dient zur Speicherung der Monate mit groesserem Umsatz for (int i=1; i < umsatz.length; i++) if (groesser(i)) zaehler++; ergebnis[j++] = i;
Wenn jetzt die entscheidende Bedingung geändert werden soll, muss man nur noch die private Methode
public boolean groesser(int index)
überarbeiten. Die Überprüfung des Kriteriums liegt nicht mehr in der Verantwortung der Analyse-Methode.
Kritiker könnten nun einwenden, warum man nicht auch die Ermittlung der Länge des Hilfsarrays in eine eigene Methode auslagert. Sicherlich könnte man das machen, man kann es mit dem Prinzip der alleinigen Verantwortung aber auch übertreiben. David Copeland schreibt in seinem Buch sogar (sarkastisch): "Single Responsibility Principle: don't write too much code in one place, in fact just make single method classes that have only one line of code in them."
In dem Artikel "Refactoring" finden Sie zwei sehr schöne Beispiele, wie lange Methoden mit mehreren Aufgaben in mehrere kürzere Methoden mit je einer Aufgabe aufgeteilt wurden.
Quellen:
- Lahres et al.: Objektorientierte Programmierung, Rheinwerk Computing 2021.
- Barnes, Kölling: Java lernen mit BlueJ - Objects first. Pearson-Verlag 2019.
- Ullenboom: Java ist auch eine Insel, Rheinwerk Computing 2023.
- Martin: SRP: The Single Responsibility Principle. Februar 1997
- Copeland: SOLID ist not SOLID - Five Object-oriented principles [...] everyone will hate". 2019 im Selbstverlag herausgegeben.