Schaltjahr: "sauberer" Code

paedubucher

Aktives Mitglied
An einer Programmiervorlesung, wo auch Codequalität thematisiert wird, wurde uns Studenten gestern eine Aufgabe gestellt. Es ging darum, eine Methode zu schreiben, die überprüft, ob ein gegebenes Jahr ein Schaltjahr ist, inkl. einiger JUnit-Tests. (Jedes vierte Jahr ist ein Schaltjahr, jedes hundertste jedoch keines, jedes vierhundertste jedoch schon.) Ich habe das ganz kurz und trocken so hingeschrieben:
Code:
public static boolean isLeap(int year) {
   return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
Nun meinte der Dozent, dass dieser Code zwar richtig, aber nicht sehr gut lesbar sei. Er schlug vor, eine Methode "isDividableBy" hinzuzufügen:
Code:
public static boolean isLeap(int year) {
   return isDividableBy(4, year) && (!isDividableBy(100, year) || isDividableBy(400, year));
}
private static boolean isDividableBy(int what, int x) {
   return x % what == 0;
}
Um das ganze noch lesbarer zu machen, sollten die einzelnen Ergebnisse in boolean-Variablen abgespeichert werden. Leider sind wir nicht mehr dazu gekommen, alle Variablen zu benennen, der Dozent meinte nur, die erste könnte regularLeapYear heissen.
Code:
public static boolean isLeap(int year) {
   boolean regularLeapYear = isDividableBy(4, year);
   boolean hundredthYear = isDividableBy(100, year);
   boolean fourHundredthYear = isDividableBy(400, year);
   return regularLeapYear && (!hundredthYear || fourHundredthYear);
}
private static boolean isDividableBy(int what, int x) {
   return x % what == 0;
}
Funktionieren tut alles, die "verbesserte" Lösung überzeugt mich aber keineswegs. Das boolean regularLeapYear suggeriert ja schon, dass es sich um ein Schaltjahr handelt ‒ und um ein "reguläres" Schaltjahr obendrein. Die anderen beiden Variablen sind meines Erachtens schwer zu benennen. Darum würde ich mir die Variablen allesamt sparen, um nicht schwammige Begriffe einführen zu müssen. (Sollte mir jemand drei gute Namen für die boolean-Variablen nennen können, revidiere ich meine Meinung gerne.)
Weiter finde ich auch schon den ersten Schritt unnötig. Wenn ich Code lese und eine Modulo-Operation sehe, deren Ergebnis anschliessend mit 0 verglichen wird, weiss ich, dass auf eine restlose Division geprüft wird. Das Einführen der isDividableBy-Methode führt nur zu einem weiteren Problem: wie benenne ich die beiden Parameter, welcher Parameter wird wofür verwendet?
Grundsätzlich verstehe ich das Ideal, Code hinschreiben zu wollen, der sich wie Prosa liest. Doch warum soll ich Prosa schreiben, wenn eine kurze Formel den Sachverhalt präziser ausdrückt? Dazu kommen weitere Probleme: Sollte die Funktion isDividable eine Eingabe-Prüfung haben? Vielleicht wird die eines Tages jemand ausserhalb meines Kontextes wieder verwenden. Wie benenne ich all die zusätzlichen Variablen?
Vielleicht habe einfach schon zu viel mit C herumgespielt, um für Java-Kreise genehmen Code zu schreiben, mir scheint aber, dass die Verbesserung des Dozenten eher gut gemeint als gut gemacht war. (Bei manchen Programmierern habe ich manchmal den Eindruck, dass die lieber "add(2, 3)" als "2 + 3" hinschreiben würden.)

Ihr denkt euch vielleicht, dass diese ganze Analyse doch etwas heavy sei für so eine primitive Aufgabenstellung. Ich finde aber, dass solche Detailfragen oft grundsätzliche Meinungsverschiedenheiten offenbaren: z.B. die Definition von "sauberem" Code: Erhalte ich diesen durch zusätzliche Indirektion? Ist die ursprüngliche oder die "verbesserte" Lösung eleganter? (Von Performance soll hier mal nicht die Rede sein...)

Danke für eure Meinungen!
 

Flown

Administrator
Mitarbeiter
Vorerst - ich weiß, dass das eine Übung ist aber trotzdem - nimmt man immer eine Funktionalität aus der Standardbibliothek, wenn sie vorhanden ist. Das garantiert, dass viele Augen und Test über die Methode gefahren sind und man sich sicher sein kann, dass die Methode funktioniert. Einfach mit:
Java:
/* Seit Java 8 DateTime API */
java.time.Year.isLeap(int n);
Wenn man es selbst schreiben muss, dann unbedingt eine Dokumentation dazuliefern. Damit steckt man die Ein- und Ausgabeparameter ab, welche Werte man zu erwarten hat, bei gewissen Eingaben.

Man sollte immer die Eingaben prüfen, außer eine Methode ist als private deklariert, dann kann man sich auch die Dokumentation sparen, denn diese wird ja nur von der Klasse als Hilfsmethode verwendet und ist nicht für die Öffentlichkeit bestimmt.

"Sauberer" Code sieht, für die meisten professionellen Entwickler, so aus (Auszug aus der Java API):
Java:
/**
* Checks if the year is a leap year, according to the ISO proleptic
* calendar system rules.
* <p>
* This method applies the current rules for leap years across the whole time-line.
* In general, a year is a leap year if it is divisible by four without
* remainder. However, years divisible by 100, are not leap years, with
* the exception of years divisible by 400 which are.
* <p>
* For example, 1904 is a leap year it is divisible by 4.
* 1900 was not a leap year as it is divisible by 100, however 2000 was a
* leap year as it is divisible by 400.
* <p>
* The calculation is proleptic - applying the same rules into the far future and far past.
* This is historically inaccurate, but is correct for the ISO-8601 standard.
*
* @param year  the year to check
* @return true if the year is leap, false otherwise
*/
public static boolean isLeap(long year) {
    return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}
Ich bin immer der Meinung, dass man unnötige Variablen vermeiden sollte, wenn man es auch kurz und prägnant halten kann - wenn Dokumentiert wurde! Darum wüsste ich auch keinen Grund warum ich für den %-Operator einen Namen (isDivisible) erfinden sollte.

ABER HALT. Was du richtig gesehen hast ist, dass es Leute gibt die gerne Sachen mit Variablen versehen und Schritte Namen geben. Das ist auch völlig in Ordnung. Für manche ist das nur eine lästige Leserei, doch für Andere eine große Erleichterung zum Verstehen des Algorithmus.
Also wie du siehst, liegt es wieder am Geschmack der Individuen und du kannst nur Argumentieren, dass es für dich leichter ist, wenn keine störenden Variablen dazwischen sind.

PS: Für Jahrhundert könntest du die Variable: isCentury einführen. Für alle 400 Jahre habe ich leider keinen Namen für dich.
 
Zuletzt bearbeitet:

paedubucher

Aktives Mitglied
Vielen Dank für die Erläuterung! Die Java-Jungs machen das ja fast wie ich, mit der Ausnahme, dass % 4 mittels & 3 umgesetzt wird. Der Name isCentury scheint mir durchaus sinnvoll gewählt. Der andere Name könnte isFourthCentury sein.
Ich muss noch anfügen, dass ich die gleiche Frage auch im http://debianforum.de/forum/viewtopic.php?f=34&t=162892 Debianforum bespreche. Der Grundtenor dort deckt sich mit deiner Erläuterung.
 

mrBrown

Super-Moderator
Mitarbeiter
Ich finde Variante zwei besser, Variante 3 würde ich nicht nehmen, Lesbarkeit gewinnt man mMn in dem Fall durch die drei zusätzlichen Variablen nicht wirklich.
Ein isDividableBy(4, 100) ist mMn deutlich Lesbarer als ein ((year &3)==0), auch wenn beides verständlich ist. Bei ersterem hat man auch zusätzliche Doku allein durch den Funktionsnamen.
Clean Code wurde ja im anderem Forum schon angesprochen, die Java-Libs sind leider kein gutes Beispiel dafür...

Bei dem Beispiel mag's überflüssig wirken, aber an so was kleinem kann man es immer am besten demonstrieren. Bei größeren sieht man dann schon eher, wenn es sinnvoll ist.
 

stg

Top Contributor
Der Name isCentury scheint mir durchaus sinnvoll gewählt.

Ist er nicht. Ein Jahrhundert/Century ist ein Zeitspanne von hundert Jahren, kein Zeitpunkt. Du würdest ja auch nicht erwarten, dass isMonth eigentlich den ersten Tag eines Monats meint.

Zu deinem eigentlichen Thema: Frage 3 Leute und du erhälst 5 unterschiedliche Meinungen.
Mir ist es wichtig, dass ich sofort vestehe, was der Code macht. Notwendig dafür sind etwa kurze Methode, sprechende Bezeichner und eine saubere Struktur des Codes. Ich würde da deine Lösung der aus der Java API vorziehen, da man bei &3 vielleicht doch erst kurz überlegen muss, was da eigentlich passieren soll.

Eine Diskussion hier wird aber nicht wirklich zielführend sein. Ich denke das in diesem Zusammenhang Wichtigste ist ohnehin bei dir angekommen. Objektiv messen lässt sich solche nicht-funktionale Code-Qualität nicht wirklich. Wichtig ist eigentlich, dass man sich dieser Problematik stets bewusst ist und immer versucht "sauberen" Code zu schreiben.

Ich kann dir auch empfehlen "Clean Code" von Onkel Bob Martin zu lesen.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
E Geburtstag im Schaltjahr berechnen Java Basics - Anfänger-Themen 24
Lion.King Schaltjahr berechnen Java Basics - Anfänger-Themen 31
J Schaltjahr Java Basics - Anfänger-Themen 6
Jamie logische Verknüpfung zur Schaltjahr-Berechnung Java Basics - Anfänger-Themen 30
Aemulit Java Schaltjahr berechnen Code Java Basics - Anfänger-Themen 7
C Programm das feststellen kann, ob eine eingegebene Zahl einem Schaltjahr entspricht, richtig geschrieben? Java Basics - Anfänger-Themen 11
Der Grütz Verständnisfrage zu Übung aus Java Kurs - Schaltjahr bestimmen Java Basics - Anfänger-Themen 2
R Schaltjahr problem Java Basics - Anfänger-Themen 10
Dawinartor Erste Schritte Schaltjahr berechnen Java Basics - Anfänger-Themen 1
S Compiler-Fehler Schaltjahr berechnen Java Basics - Anfänger-Themen 5
C Schaltjahr Java Basics - Anfänger-Themen 4
T Wenn Schaltjahr ist, soll Februar, einen Tag mehr haben, GUI mit combobox Java Basics - Anfänger-Themen 4
B Schaltjahr überprüfen (Code) Java Basics - Anfänger-Themen 4
U Java Quellcode Schaltjahr Java Basics - Anfänger-Themen 13
G Schaltjahr Java Basics - Anfänger-Themen 6
W Schaltjahr abbrechen wenn <1583 Java Basics - Anfänger-Themen 11
W Schaltjahr Berechnung Java Basics - Anfänger-Themen 24
S schaltjahr (gelöst zu 90 %), allerdings folgeprob :/ Java Basics - Anfänger-Themen 18
K Sauberer Programmierstil? Java Basics - Anfänger-Themen 3
E Mein erstes Java Projekt - Sauberer code? Java Basics - Anfänger-Themen 28
D Was ist performanter und/oder sauberer? Java Basics - Anfänger-Themen 8
F Sauberer Programmierstyle: Klassenvariaben in Methoden benutzen? Java Basics - Anfänger-Themen 5
S sauberer Stil von return Wert (try, catch, finally) Java Basics - Anfänger-Themen 9
T Kann mir jemand wörtlich erklären, was in dem Code genau passiert? Java Basics - Anfänger-Themen 1
Ü Dead Code im Programm? Java Basics - Anfänger-Themen 13
I QR code in Java selber generieren Java Basics - Anfänger-Themen 5
terashy VS Code Project run error Java Basics - Anfänger-Themen 10
JaZuDemNo Code Erklärung Java Basics - Anfänger-Themen 3
M Connect-4-Code analysieren Java Basics - Anfänger-Themen 2
N BMI Rechner Was haltet ihr von dem Code habt ihr Verbesserungsvorschläge weil design teschnisch ist das nicht das geilste würde das gerne überarbeiten Java Basics - Anfänger-Themen 12
W In alten Code zurück- und dort wieder zurechtfinden? Java Basics - Anfänger-Themen 17
T code so schreiben das er von sich selber anpasst (code soll die anzahl aller bustaben bestimmen) Java Basics - Anfänger-Themen 16
J Frage zu einem "Taschenrechner" code Java Basics - Anfänger-Themen 9
T Fehlercode bei code der das Alter ausrechnet Java Basics - Anfänger-Themen 2
T Text einlesen code was kommt dahin? Java Basics - Anfänger-Themen 1
jhfjeh Strukturgramm in code Java Basics - Anfänger-Themen 11
D Tipps zum Code Java Basics - Anfänger-Themen 24
W Java-Code mit Array Java Basics - Anfänger-Themen 14
W Java-Code Java Basics - Anfänger-Themen 2
W Java code- TicTac toe Java Basics - Anfänger-Themen 51
W Java-code Java Basics - Anfänger-Themen 8
W Java-code Java Basics - Anfänger-Themen 9
W Java-Code erklären Java Basics - Anfänger-Themen 6
ohneInformatik; For Schleife. Was macht dieser Code?? Java Basics - Anfänger-Themen 5
Say Fehlenden Code finden in einer while-Schleife? Java Basics - Anfänger-Themen 11
Say 2-DIM Array Code lesen und verstehen Java Basics - Anfänger-Themen 5
Say Stelle in Code herausfinden, wie geht man vor? Java Basics - Anfänger-Themen 12
Say do-While Code Ausführung Java Basics - Anfänger-Themen 3
W Rückfrage zur Programmgestaltung (clean code) Java Basics - Anfänger-Themen 12
M intelliJ auf neuem PC, plötzlich kein Code Java Basics - Anfänger-Themen 3
Pinhg Sound in Greenfoot Code einbinden Java Basics - Anfänger-Themen 2
C Java boolean Code läuft nicht Java Basics - Anfänger-Themen 5
I Code für Bezahlsystem (auch bei Offline Aktivität) Java Basics - Anfänger-Themen 7
J Größter gemeinsamer Teiler: mein Code Java Basics - Anfänger-Themen 6
B Den Dateipfad einer Java Datei durch Code in Selbiger finden? Java Basics - Anfänger-Themen 10
A Wie könnte man diesen Code kürzer machen ? Java Basics - Anfänger-Themen 7
J Frage zu meinem Code (OOP) Java Basics - Anfänger-Themen 4
Alen123 Warum funktioniert mein Code nicht? Java Basics - Anfänger-Themen 64
Max246Sch Frage zu Währungsrechner Code Java Basics - Anfänger-Themen 2
S Hilfe bei Umänderung von Java Code Java Basics - Anfänger-Themen 16
I Code wird nicht ausgeführt Java Basics - Anfänger-Themen 2
K Wie kann man diesen Code schnell und effizient interpretieren (Man hat nur 4 Minuten) Java Basics - Anfänger-Themen 3
R ISBN-10-Code überprüfen Java Basics - Anfänger-Themen 7
I Bitte um Hilfe zu unterstehenden Code Java Basics - Anfänger-Themen 6
I Interface von einer EJB Klasse, um Code zu reduzieren Java Basics - Anfänger-Themen 1
I HTML Code säubern Java Basics - Anfänger-Themen 4
B Brauche Hilfe zu einem Code Java Basics - Anfänger-Themen 5
Temsky34 Problem mit dem Code Java Basics - Anfänger-Themen 17
N Fehler im Code (Aufgabe für Anfänger) Java Basics - Anfänger-Themen 11
N Java-Code abwärtskompatibel machen Java Basics - Anfänger-Themen 4
J Erste Schritte Was mache ich in meinem Code falsch. Java Basics - Anfänger-Themen 3
Ameise04 Variablen Inhalt einer Variable im Code verwenden? Java Basics - Anfänger-Themen 9
S Compiler-Fehler Nicht adressierbarer Code ( Non-addressable code ) Java Basics - Anfänger-Themen 5
A Code Problem Java Basics - Anfänger-Themen 6
C Fehler im Code Java Basics - Anfänger-Themen 10
A Zu einem bestimmten Ort im Code springen Java Basics - Anfänger-Themen 11
L Ist der Code richtig Java Basics - Anfänger-Themen 3
josfe1234 code vereinfachen Java Basics - Anfänger-Themen 15
nonickatall Ausführbarkeit von Code testen bzw. Remote Debugging Java Basics - Anfänger-Themen 4
F Frage betreff Programm mit dem man C++-Code in JAVA-Code übersetzen lassen kann Java Basics - Anfänger-Themen 2
S Fehler bei Code mit SubStrings für mich nicht auffindbar. Java Basics - Anfänger-Themen 4
G Programm Code Java Basics - Anfänger-Themen 5
C Code zusammenfassen Java Basics - Anfänger-Themen 5
I Erklärung zum Java Code Java Basics - Anfänger-Themen 2
T Programmablaufsplaninterpretation in Code umformen Java Basics - Anfänger-Themen 1
dieter000 Kurze Frage kann mir ejmand kurz diesen Code erklären, bzw wie man die zeilen erklärt und so Java Basics - Anfänger-Themen 1
AlexVo String zu Java Anweisung getString("*** java code ***") Java Basics - Anfänger-Themen 19
M ISBN-Code Java Basics - Anfänger-Themen 26
B Zeitgleiches Arbeiten am Code mit mehreren Personen? Java Basics - Anfänger-Themen 7
S Wie kann ich bei diesem Code erreichen, das als Ergebnis hier 15 herauskommt? Java Basics - Anfänger-Themen 23
N Kann man den Code vereinfachen? Java Basics - Anfänger-Themen 25
marcooooo Code erklären Java Basics - Anfänger-Themen 28
marcooooo Code erklären Java Basics - Anfänger-Themen 4
S Advent of Code Day4 Java Basics - Anfänger-Themen 4
B Nach eingefügtem Code erkennt Compiler keine Instanzvar und meldet SyntaxError Java Basics - Anfänger-Themen 2
Gaudimagspam Caesars Code entziffern in Java Java Basics - Anfänger-Themen 8
Lukasbsc Wie kann ich meinen Code optimieren? Java Basics - Anfänger-Themen 4
NeoLexx equals()-Methode Verständnis Frage anhand Code Beispiel Java Basics - Anfänger-Themen 22
I Input/Output Code wird doppelt ausgeführt Java Basics - Anfänger-Themen 3
T Main startet nicht bei vorgegebenen Code Java Basics - Anfänger-Themen 41

Ähnliche Java Themen

Neue Themen


Oben