Komplexität
🚧 | Diese Seite befindet sich in Bearbeitung | 🚧 |
🤓 | Diese Seite ist eine Bewertungsrichtlinie, die ab Blatt 1 annotiert und ab Blatt 2 abgezogen wird. | 🤓 |
Beschreibung
Ein wichtiges Ziel beim Programmieren ist, dass der geschriebenen Quellcode einfach von anderen verstanden werden kann. Deshalb sollte immer der einfachste Weg gewählt werden, um eine vorgegebene Funktionalität zu implementieren. Dies bedeutet nicht, dass der Quellcode aus möglichst wenigen Anweisungen und Zeichen bestehen soll (vgl. Code Golf), sondern dass auf unnötige Konstrukte, innerhalb der Vorgaben des Checkstyle und der weiteren Bewertungskriterien, verzichtet werden soll.
Möglichkeiten, Code zu vereinfachen, werden häufig erst im Laufe der Entwicklung sichtbar. Beim Überarbeiten und Weiterentwickeln sollte stets ein Augenmerk darauf gelegt werden, ob der bestehende Code unnötige Komplexität enthält und einfacher und verständlicher gestaltet werden kann.
Verschachtelungstiefe
Die Logik eines Programms wird hauptsächlich durch Kontrollstrukturen gegeben. Diese lassen sich auch ineinander schreiben um dadurch sehr komplexe Logiken zu implementieren. Für das Verschachteln gibt es allerdings Regeln, sodass der Code dadurch nicht unleserlich oder gar unverständlich wird.
Generell gilt:
Kleine Methoden sind besser zu lesen als große Methoden. In kleineren Methoden fallen zudem Fehler besser auf und Sichtbarkeiten können geeigneter gewählt werden (z.B. private Hilfsmethoden). Geeignete Methodennamen ermöglichen es, selbst komplexe Logiken, leicht verständlich zu implementieren.
Negativbeispiel
public static List<Integer> primesBad(final int max) {
final List<Integer> primes = new ArrayList<>();
for(int n = 1; n <= max; n++) {
int divisors = 0;
for(int i = 1; i <= n; i++) {
if(n % i == 0) {
divisors++;
}
}
if(divisors == 2) {
primes.add(n);
}
}
return primes;
}
- Das Beispiel ist für die Verständlichkeit hier nicht zu komplex gewählt
- Es muss verstanden werden, wie sich der Zustand von primes und divisors entwickelt
- Bei kompleceren Problemen wird das schnell anstrengend und fehleranfällig
Positivbeispiel
public static List<Integer> primesGood(final int max) {
final List<Integer> primes = new ArrayList<>();
for(int n = 1; n <= max; n++) {
if(isPrime(n)) {
primes.add(n);
}
}
return primes;
}
private static boolean isPrime(final int n) {
int divisors = 0;
for(int i = 1; i <= n; i++) {
if(divides(i, n)) {
divisors++;
}
}
return divisors == 2;
}
private static boolean divides(final int divisor, final int dividend) {
return dividend % divisor == 0;
}
- Die beiden Hilfsmethoden helfen bei der Erklärung was passiert bereits durch ihre gewählten Bezeichner
- Jede Methode hat höchstens einen veränderlichen Zustand, der nach der Ausführung vergessen werden kann
- Die Korrektheit jeder einzelnen Funktion ist offensichtlich (für n > 0)
Bei der Bewertung unnötiger Komplexität unterscheiden wir zwei Schweregrade - klein und groß.
Kleine Fälle unnötiger Komplexität sind Code innerhalb einer Methode oder eine ganze Methode, die zu komplex gestaltet wurde. Dazu zählen unter anderem Boolesche Ausdrücke in if
-Statements, die nicht vereinfacht wurden.
Große Fälle unnötiger Komplexität treten methoden- oder klassenübergreifend auf.
Beide Kategorien werden als eigene Richtlinie behandelt und geben demnach separat Abzug.
Negativbeispiel
boolean isValid() {
if (this.sold == false && !(this.price <= 0)) {
return true;
}
return false;
Der Ausdruck ist hier unnötig komplex und kann vereinfacht werden. this.sold == false
entspricht dem Ausdruck !this.sold
(!
invertiert this.sold
, entsprechend ist der Ausdruck nur wahr, wenn this.sold
false ist).
Entsprechend ist this.price > 0
eine vereinfachte Variante von !(this.price <= 0)
.
Positivbeispiel
boolean isValid() {
return !this.sold && this.price > 0;
}
Hier wurde der Ausdruck entsprechend vereinfacht, sodass er leichter zu lesen und verstehen ist.
Wenn du diese Seite interessant fandest, findest du hier noch mehr Seite(n) dazu:
Assertions, Duplikate, Gottklasse, Hartcodieren, IO/UI, Reimplementierung, Ungeeigneter Schleifentyp