Schaltjahr: "sauberer" Code

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Schaltjahr: "sauberer" Code

Beitrag von paedubucher » 07.11.2016 22:07:37

An einer Programmiervorlesung, wo auch Codequalität thematisiert wird, wurde uns Studenten heute 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: Alles auswählen

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: Alles auswählen

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: Alles auswählen

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 begeisterten Java-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! (Ihr könnt ja noch dazu schreiben, ob ihr eher aus der Assembler- oder aus der Visual-Basic-Ecke kommt :wink: )
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
whisper
Beiträge: 3192
Registriert: 23.09.2002 14:32:21
Lizenz eigener Beiträge: GNU Free Documentation License
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von whisper » 07.11.2016 22:13:59

Deine Erstlösung ist doch sehr kurz, dennoch lesbar. jedenfalls für C-Programmierer.
Endlich mal eine sinnvolle Anwendung für Logisch AND/OR in einem nicht Controller Umfeld.
Java ist was für "ich google mal schnell, ob's da nicht 'ne lib für gibt" Programmierer :twisted:

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Schaltjahr: "sauberer" Code

Beitrag von hikaru » 07.11.2016 23:14:45

Ich finde ebenfalls, dass deine erste Variante die Lesbarste ist, da sie für jeden unmittelbar ersichtlich ist, der mit logischen Operatoren umgehen kann.
Von Prosa-Quelltext halte ich ehrlich gesagt nichts. Sprechende Variablennamen sind gut und richtig, aber wenn man gar keine Variablen braucht, dann erhöht ihre Einführung mMn nicht die Lesbarkeit. (Ausnahmen bestätigen die Regel, aber wir sind ja hier nicht bei Perl. ;) )

Die Einführung einer zusätzlichen Methode für so eine banale Problemstellung halte ich sogar für schädlich für die Lesbarkeit. Deine ursprüngliche Lösung hatte bereits der "Generic-Code-Interpreter" in meinem Hirn intuitiv beim Überfliegen geparst. Die Frage um welche Programmiersprache es geht, stellte sich erst beim zweiten Code-Block.
Ich bin übrigens sowohl aus C als auch aus Java zu lange raus, um irgendeiner Präferenz verdächtig zu sein.

Wenn Prosa gewünscht ist, dann würde ich lieber einen Kommentarblock mit klarem Bezug zum Code voranstellen. Da kann man dann gern auch unterbringen, wie die logischen Verknüpfungen funktionieren, wie ein Schaltjahr im Prinzip berechnet wird und eine kurze Geschichte des gregorianischen Kalenders anfügen.

Ich möchte deinem Dozenten nicht zu nahe treten, aber wenn er diese Aufblähung deines Codes aus Lesbarkeitsgründen wollte, dann sollte er mMn mal aus dem Lehrdunst in die reale Programmiererwelt hinaustreten, denn seine Lesbarkeitskriterien wären offensichtlich praxisfern.
Ich vermute allerdings, dass es ihm nicht wirklich um die Lesbarkeit ging, sondern darum zu prüfen, ob du das Problem auf mehr als eine Weise lösen kannst, vorzugsweise auch auf die vom Lehrplan geforderte. Das sollte er mMn dann aber auch so sagen, um dir nicht aus Versehen einzureden, dein ursprünglicher Code wäre verbesserungsbedürftig.

Benutzeravatar
MSfree
Beiträge: 10776
Registriert: 25.09.2007 19:59:30

Re: Schaltjahr: "sauberer" Code

Beitrag von MSfree » 08.11.2016 08:02:06

hikaru hat geschrieben:Ich finde ebenfalls, dass deine erste Variante die Lesbarste ist
ACK
Die Einführung einer zusätzlichen Methode für so eine banale Problemstellung halte ich sogar für schädlich für die Lesbarkeit.
Und schädlich für die Geschwindigkeit. OK, so eine Schaltjahrfunktion wird man nicht millionenafch pro Sekunde aufrufen. So eine Einstellung zum Programmieren in die reale Welt des Numbercrunching übertragen führt dann zu Bloatcode, der nicht nur viel mehr Platz beansprucht sondern auch nur mit 1/3 der Geschwindikgiet läuft wie optimal kurzer Code.

Und wenn es um die Lesbarkeit geht, so könnte man den ursprünglichen Code auch so schreiben:

Code: Alles auswählen

public static boolean isLeap(int year) {
   return year % 4 == 0 &&   // alle 4 Jahre
         (year % 100 != 0 || // alle 100 Jahre nicht
          year % 400 == 0);  // jedoch alle 400 Jahre doch ;)
}

DeletedUserReAsG

Re: Schaltjahr: "sauberer" Code

Beitrag von DeletedUserReAsG » 08.11.2016 08:07:36

+1 zur ersten Variante. Die kann selbst ich als Nicht-C-Programmierer nachvollziehen und damit darf sie als lesbar gewertet werden. Die letzte Variante ist zumindest für mich deutlich schwerer nachzuvollziehen.

Benutzeravatar
TRex
Moderator
Beiträge: 8079
Registriert: 23.11.2006 12:23:54
Wohnort: KA

Re: Schaltjahr: "sauberer" Code

Beitrag von TRex » 08.11.2016 08:27:07

+1 zur ersten Variante - das ist nicht erklärungsbedürftig.

Code: Alles auswählen

for i in `seq 1 2 $((2*$1-1))`;do echo $((j+=i));done
Wäre eher etwas, das ich eventuell zerlegen/umformen würde - alleine schon wegen der Verwendung von ``.
Jesus saves. Buddha does incremental backups.
Windows ist doof, Linux funktioniert nichtDon't break debian!Wie man widerspricht

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Schaltjahr: "sauberer" Code

Beitrag von hikaru » 08.11.2016 09:13:48

MSfree hat geschrieben:
Die Einführung einer zusätzlichen Methode für so eine banale Problemstellung halte ich sogar für schädlich für die Lesbarkeit.
Und schädlich für die Geschwindigkeit.
Falls du die Arbeitsweise des Java-JIT diesbezüglich tatsächlich beurteilen kannst, dann will ich nichts gesagt haben, aber auf der CppCon gab es dieses Jahr einen Vortrag [1], der mMn gut demonstriert, dass man als Laie (als der ich mich hier bezeichnen würde) nicht wirklich die Performance eines High-Level-Codes beurteilen kann.

[1] https://www.youtube.com/watch?v=zBkNBP00wJE

Benutzeravatar
MSfree
Beiträge: 10776
Registriert: 25.09.2007 19:59:30

Re: Schaltjahr: "sauberer" Code

Beitrag von MSfree » 08.11.2016 09:26:47

hikaru hat geschrieben:Falls du die Arbeitsweise des Java-JIT diesbezüglich tatsächlich beurteilen kannst, dann will ich nichts gesagt haben, aber auf der CppCon gab es dieses Jahr einen Vortrag [1], der mMn gut demonstriert, dass man als Laie (als der ich mich hier bezeichnen würde) nicht wirklich die Performance eines High-Level-Codes beurteilen kann.
Ein Funktionsaufruf bedeutet immer mehr Aufwand als direktes abarbeiten von Anweisungen. Natürlich sind die Optimierer in den Compilern inzwischen sehr gut und ersetzen Funktionsaufrufe durch Inline-Code, das glingt aber nicht immer. Man sollte sich jedenfalls nicht auf Optimierungsfähigkeiten verlassen, optimierter Quellcode ist auch für den Compiler einfacher und effektiver in optimalen Maschinencode zu üebrsetzen als verschwurbelter Prosamist. Daher gilt auch aus langjähriger Erfahrung, daß man lieber kürzeren mit weniger Umwegen behafteten Quellcode schreiben sollte als sich auf JIT und/oder Optimierung im Compiler zu verlassen.

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von paedubucher » 08.11.2016 09:47:23

Vielen Dank für die vielen Rückmeldungen! Es ist für mich wichtig zu hören, dass meine Lösung gut verständlich ist. Zu meinem Code habe ich bisher selten Rückmeldung bekommen, gestern war einer der wenigen Fälle. Da fragte ich mich dann schon, ob ich nicht vielleicht etwas autistisch veranlagt sei, und mich einfach nicht in die Rolle des Code-Lesers einfühlen könne :wink:
Den Performance-Aspekt wollte ich absichtlich nicht ansprechen, zumal ich nicht weiss, was da der Java-Compiler alles optimiert, das selbe gilt für gcc -O2.
Noch ein Nachtrag zum Dozenten, der meinen Code besprochen hat: Das ist nicht der eigentliche Professor, das ist einer aus der Praxis (Java/C#), der vom Professor für diese Vorlesung herangezogen wurde. Der Professor sitzt dann meistens in der letzten Reihe und trägt oftmals etwas zur Diskussion bei, in diesem Fall hat er sich aber rausgehalten. Das ganze wirkt auf mich wie etwas ein Vorlesungs-Praktikum für den Herrn, und vielleicht ist er dadurch auch manchmal etwas übermotiviert im Code"verbessern". Wenn man schon bezahlt wird, muss man doch schliesslich auch etwas tun :wink:
Jetzt mache ich mal etwas ganz infames: Ich eröffne denselben Thread in einem Java-Forum; mal schauen, wie dort die Rückmeldungen aussehen werden :mrgreen:
Nachtrag: Hier ist der Thread. Jetzt lehne ich mich zurück und warte darauf, dass jemand den Code von TimespanPredicateCheckerFactoryYearImpl postet :lol:
Zuletzt geändert von paedubucher am 08.11.2016 09:54:32, insgesamt 1-mal geändert.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: Schaltjahr: "sauberer" Code

Beitrag von Lord_Carlos » 08.11.2016 09:52:35

Mir gefaellt die zweite Loesung am besten. Um so komplexer die Problemstellung vielleicht sogar Loesung #3.

Hier weist du vorher schon was passieren soll, und kennst du mit Schaltjahren aus. Aber stell dir vor jemand liest es der sich mit dem Konzept der Jahre nicht gut auskennt. Klinkt jetzt erstmal komisch, aber ich Schreibe beruflich Software fuer Krankenhaeuser, kann mich aber nicht erinnern wann ich das letzte mal in einem war :P

Unser Projekt / Produkt ist 10 Jahre alt, unser Code muss also lange lesbar sein. Wenn du jetzt eine Methode rauspickst mit einem kleinen Konzept mit dem sich jeder schon auskennt, dann wirkt es wirklich etwas laecherlich. Ist es aber nicht.

In deinem Beispiel nicht so wichtig, aber um so groesser / laenge die Methode wird um so wichtiger ist es den Code in weitere Methoden aufzuteilen. Ich glaube dein Dozent will es dir das richtige beibringen, aber das beispiel hingt etwas.

Kommentare wie in der Loesung von MSfree sind bei uns nicht erlaubt. (mit sehr wenigen Ausnahmen)
Kommentare werden oft nicht gepflegt. Dann steht da auf einmal ein anderer Kommentar als das was wirklich passiert. Was dazu folgt das den keiner Mehr vertraut. Immer wenn ich denke "Das sollte ich kommentieren"

Dazu wurden ganze Buecher geschrieben. Siehe "Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin) "
Das buch kann ich empfehlen.

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von paedubucher » 08.11.2016 10:08:12

Lord_Carlos hat geschrieben:Mir gefaellt die zweite Loesung am besten. Um so komplexer die Problemstellung vielleicht sogar Loesung #3.
Die dividableBy-Methode gibt der %x==0-Operation einen Namen. Von daher kann ich schon nachvollziehen, wenn jemand lieber so eine Methode drin haben möchte. Es fragt sich aber auch, wer denn den Code lesen soll. Ein Anfänger kommt mit der Methode sicher besser zurecht. Ein fortgeschrittener Programmierer hätte wohl lieber die Blanke %x==0-Operation.
Lord_Carlos hat geschrieben: Hier weist du vorher schon was passieren soll, und kennst du mit Schaltjahren aus. Aber stell dir vor jemand liest es der sich mit dem Konzept der Jahre nicht gut auskennt.
Natürlich gehört bei produktivem Code noch ein JavaDoc-Kommentar obendrüber, der den Sachverhalt erklärt oder gar eine belastbare Quelle zitiert. Dazu reichte aber gestern die Übungszeit nicht aus. Grundsätzlich finde ich es schön, den Sachverhalt in Code und nicht im Kommentar klar auszudrücken. Aber eine belastbare Quelle zu verlinken, kann nicht schaden.
Lord_Carlos hat geschrieben: In deinem Beispiel nicht so wichtig, aber um so groesser / laenge die Methode wird um so wichtiger ist es den Code in weitere Methoden aufzuteilen. Ich glaube dein Dozent will es dir das richtige beibringen, aber das beispiel hingt etwas.
Das ist ein grundsätzliches Problem an dieser Vorlesung. Natürlich sehe ich die Notwendigkeit, den Code in kleine Funktionseinheiten aufzuteilen. Ich bin ein UNIX-Fan, von daher bin ich in dieser Beziehung recht extrem. Solche trivialen Beispiele eignen sich aber meiner Meinung nach überhaupt nicht zur Demonstration. Es geht darum, Spaghetticode und Wiederholungen zu vermeiden. Ein Einzeiler mit einer Zeilenbreite von 64 Zeichen ist kein Spaghetticode. Und isDividableBy(4, year) ist länger als year % 4 == 0. Wiederholungen lassen sich mit dieser Methode auch nicht vermeiden. Das Umbrechen der Zeile ist aber durchaus eine Möglichkeit.
Lord_Carlos hat geschrieben: Dazu wurden ganze Buecher geschrieben. Siehe "Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin) "
Das buch kann ich empfehlen.
Das habe ich gerade letzte Woche zu lesen angefangen. :wink:
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
MSfree
Beiträge: 10776
Registriert: 25.09.2007 19:59:30

Re: Schaltjahr: "sauberer" Code

Beitrag von MSfree » 08.11.2016 10:21:18

Lord_Carlos hat geschrieben:Kommentare wie in der Loesung von MSfree sind bei uns nicht erlaubt. (mit sehr wenigen Ausnahmen)
Frei nach dem Motto:

Never put comments in your code!
It was hard to develope so it ought to be hard to understand.
Kommentare werden oft nicht gepflegt.
Das ist allerdings ein Problem.

Ich bin auch kein großer Freund von Kommentaren. Ich bin aber auch nicht böse darum, wenn um ein kurios erscheinendes Kontrukt dann doch ein Kommentar steht, warum man es so und nicht anders gelöst hat, weil die vermeintlich andere, einfachere Lösung Nebenwirkungen hat, die man genau mit der Kuriosität vermeidet.

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Schaltjahr: "sauberer" Code

Beitrag von hikaru » 08.11.2016 10:24:24

MSfree hat geschrieben:Ein Funktionsaufruf bedeutet immer mehr Aufwand als direktes abarbeiten von Anweisungen.
Eben das stimmt nicht, und zwar deshalb:
MSfree hat geschrieben:Natürlich sind die Optimierer in den Compilern inzwischen sehr gut und ersetzen Funktionsaufrufe durch Inline-Code,
Dass Code ohne Funktionen schneller ist, taugt daher höchstens als grobe Faustregel, muss sich im Einzelfall aber einer genauen Überprüfung stellen.
MSfree hat geschrieben:optimierter Quellcode ist auch für den Compiler einfacher und effektiver in optimalen Maschinencode zu üebrsetzen als verschwurbelter Prosamist.
Wieder: Wenn du die Fachkenntnis hast um das zu beurteilen, also weißt wie ein bestimmter Compiler im Detail arbeitet, dann glaube ich dir das einfach. Aber als allgemeine Aussage halte ich das für zu platt.

paedubucher hat geschrieben:Jetzt mache ich mal etwas ganz infames: Ich eröffne denselben Thread in einem Java-Forum; mal schauen, wie dort die Rückmeldungen aussehen werden :mrgreen:
Sei bitte so nett und verweise auch in dem anderen Thread auf den Thread hier im Debianforum!
Crosspostings kenntlich zu machen ist einerseits höflich, und führt ganz praktisch manchmal zu Synergieeffekten.

Lord_Carlos hat geschrieben:Mir gefaellt die zweite Loesung am besten.
Was gewinnst du dort gegenüber der ersten Lösung?
Lord_Carlos hat geschrieben:Unser Projekt / Produkt ist 10 Jahre alt, unser Code muss also lange lesbar sein.
Ich arbeite mit Code der teilweise 40 Jahre alt ist. Und gerade da sind Kommentare unglaublich wertvoll, denn die Originalautoren sind für gewöhnlich schon in Rente, manche auch schon tot.
Lord_Carlos hat geschrieben:Kommentare werden oft nicht gepflegt. Dann steht da auf einmal ein anderer Kommentar als das was wirklich passiert. Was dazu folgt das den keiner Mehr vertraut. Immer wenn ich denke "Das sollte ich kommentieren"
Dass man Kommentaren nicht vertrauen kann stimmt leider. Aber sie können zumindest einen Hinweis darauf geben, was sich der Autor dabei gedacht hat (das ist teilweise wichtiger als zu beschreiben was tatsächlich passiert). Ob dann Kommentar und Code zusammenpassen muss sich herausstellen, aber mit einer vom Kommentar erweckten Erwartung an einen Code heranzugehen kann hilfreich sein.
Was Kommentare nicht tun sollten, ist den Code zu wiederholen, also sollte der Kommentar in der vorliegenden Aufgabe z.B. nicht so aussehen:

Code: Alles auswählen

// Dividiere durch 4, wenn Rest = 0 dann prüfe ob nicht durch 100 teilbar oder durch 400 teilbar.
Kommentare sollten beschreiben, warum ein Code eine bestimmte Aktion macht:

Code: Alles auswählen

// Für Problem X müssen Schaltjahre berücksichtigt werden.
// Schaltjahre sind durch 4 teilbar. Durch 100 teilbare Jahre sind jedoch keine Schaltjahre. Durch 400 teilbare Jahre wiederum sind Schaltjahre.
Diese Struktur sollte der Programmierer dann selbst im Code erkennen können, wobei mMn Einfachheit vor Ausführlichkeit geht, und wenn man es sich leisten kann auch vor Performance.

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: Schaltjahr: "sauberer" Code

Beitrag von Lord_Carlos » 08.11.2016 10:41:52

paedubucher hat geschrieben:
Lord_Carlos hat geschrieben: Dazu wurden ganze Buecher geschrieben. Siehe "Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin) "
Das buch kann ich empfehlen.
Das habe ich gerade letzte Woche zu lesen angefangen. :wink:
Das sollte die meisten deiner Fragen beantworten. Ich bin leider nicht so gut dadrinne mich auszudruecken, aber das Buch erklaert das alles gut mit netten Beispielen. Auch die Sache mit den Kommentaren. Es geht nicht dadrum Kommentare auf keinen Fall zu schreiben, sondern erstmal zu versuchen gut lesbaren code mit verstaendlichen Variablen und Methoden Namen zu schreiben.

Gerade vor wenigen Minuten habe ich Code review fuer einen Arbeitskollegen gemacht wo der Code hatte auch Kommentare enthielt. In diesem Fall zu recht.

____________

In java keine neue Methode oder Klasse zu erstellen aus Performansgruenden kann ich nicht verstehen. Wenn eine Methode mehr als ~15 Zeilen lang ist sollte man versuchen die aufzuteilen.

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von paedubucher » 08.11.2016 11:41:21

Ich habe den Crosspost jetzt auch auf dem Javaforum vermerkt, danke für den Hinweis.
Dort wurde mir gerade mitgeteilt, dass diese Methode seit Java 8 zur Standardlibrary gehört:

Code: Alles auswählen

public static boolean isLeap(long year) {
    return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}
Das ist fast identisch mit meinem Code, nur wurde Modulo 4 mit einem bitweisen Und umgesetzt. Da muss man einen Moment überlegen. Das geht mir schon zu stark in die Richtung cleverer Hack, das sollte man meiner Meinung nach besser dem Compiler überlassen. Aber die Java-Jungs kennen die Optimierungen sicher besser als ich.
Lustig finde ich, dass ein Java-Experte (der Dozent) den Code der Java-Library überarbeiten würde. Diese Diskrepanz ist mir im Java-Bereich schon oft aufgefallen.
Zuletzt geändert von paedubucher am 08.11.2016 11:49:55, insgesamt 1-mal geändert.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: Schaltjahr: "sauberer" Code

Beitrag von Lord_Carlos » 08.11.2016 11:48:50

paedubucher hat geschrieben: Lustig finde ich, dass ein Java-Experte (der Dozent) den Code der Java-Library überarbeiten würde.
Ich glaube eher er hat nach einer Aufgabe gesucht um euch etwas beizubringen. Der Inhalt ist nicht so wichtig, es geht um die Problem Loesung.
Das ist jedenfalls meine Vermutung.

Schaltjahr, Zeitumstellung und Zeitzonen selber zu implementieren sollte man so oder so vermeiden. Bekanntes Video dazu (Auf English) https://www.youtube.com/watch?v=-5wpm-gesOY

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

Benutzeravatar
paedubucher
Beiträge: 856
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von paedubucher » 08.11.2016 12:18:44

Lord_Carlos hat geschrieben: Ich glaube eher er hat nach einer Aufgabe gesucht um euch etwas beizubringen. Der Inhalt ist nicht so wichtig, es geht um die Problem Loesung.
Das ist jedenfalls meine Vermutung.
Das mag schon sein. Aber dann bleibe ich dabei: gut gemeint, weniger gut gemacht.
Lord_Carlos hat geschrieben: Schaltjahr, Zeitumstellung und Zeitzonen selber zu implementieren sollte man so oder so vermeiden. Bekanntes Video dazu (Auf English) https://www.youtube.com/watch?v=-5wpm-gesOY
Das kenne ich schon, es ist in der Tat eine grossartige Erklärung!
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Meillo
Moderator
Beiträge: 8818
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von Meillo » 08.11.2016 12:31:29

paedubucher hat geschrieben:

Code: Alles auswählen

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.
Das ist der typische Kommentar von mittelpraechtigen Programmierern ...

Gute Programmierer erkennen in obigem Code ein Idiom, weil sie genau dieses Pattern schon x-mal gesehen haben und auf einem Blick erfassen und verstehen koennen. Damit wird der Code -- ganz im Gegenteil zur Meinung des Dozenten -- viel leichter lesbar, denn er kann auf einen Blick erfasst werden!

(Ob die == bzw. != und die && bzw. || nun richtig sind oder nicht, das ist in keiner Umsetzung einfach zu pruefen.)
Use ed once in a while!

Benutzeravatar
hikaru
Moderator
Beiträge: 13593
Registriert: 09.04.2008 12:48:59

Re: Schaltjahr: "sauberer" Code

Beitrag von hikaru » 08.11.2016 12:53:15

offtopic:
Die Jungs aus dem Javaforum könnten mal ihre Code-Blöcke überarbeiten.
Solch unnötig schmales Website-Design, das im Vollbild nur 1/3 des Monitors füllt finde ich eh schon ungünstig, aber dann Code-Blöcke in einem Programmierfoum(!) nochmal auf die Hälfte dessen zu reduzieren hätte ich bis eben für einen verirrten Aprilscherz gehalten. DAS ist unlesbarer Code.

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: Schaltjahr: "sauberer" Code

Beitrag von Lord_Carlos » 08.11.2016 13:24:40

Nochmal drueber nachgedacht finde ich eine Erweiterung von Loesung #2 am besten:

Code: Alles auswählen

    public static boolean isLeap(int year) {
        return isDividableBy(4, year) && isCenturyLeap(year);
    }

    private static boolean isCenturyLeap(int year) {
        return !isDividableBy(100, year) || isDividableBy(400, year);
    }

    private static boolean isDividableBy(int what, int x) {
        return x % what == 0;
    }
So hat man das wichtigste auf einer Linie, ohne es so voll zu machen wie in Loesung #3
Man kann jedoch schnell in die details eintauchen wenn es wichtig ist.
Und leichter veraendern wenn z.B. leap Century Berechnung irgendwann mal komplexer wird.
Meillo hat geschrieben: Das ist der typische Kommentar von mittelpraechtigen Programmierern ...
Das ist doch mal eine Aussage.

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

Liffi
Beiträge: 2306
Registriert: 02.10.2004 01:33:05

Re: Schaltjahr: "sauberer" Code

Beitrag von Liffi » 08.11.2016 13:29:46

Ich finde bei den drei wirklich überschaubaren Bedingungen Lösung 2 am besten.
Zusätzlich bin ich auch kein Freund von statischen Methoden. Auch wenn die hier natürlich überschaubar sind und keine Seiteneffekte haben. Sie erschweren das Testen einfach unnötig. (Ja, manche statische Methoden ergeben sicher Sinn, z.B. in der Math Klasse).

Benutzeravatar
Meillo
Moderator
Beiträge: 8818
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von Meillo » 08.11.2016 14:13:37

MSfree hat geschrieben: Und schädlich für die Geschwindigkeit.
Wer in Fragen der Code-Qualitaet die Rechner-Performance anfuehrt, verliert. (Ihr duerft das gerne Meillo's Law nennen.)

In den allermeisten Faellen (also in sehr viel mehr Faellen als man gemeinhin denkt) ist die Code-Qualitaet (und damit die Programmierer-Performance) soviel bedeutender als die Rechner-Performance.


Was fuer ein super Beispiel hier auch gleich zur Hand ist:
paedubucher hat geschrieben: Dort wurde mir gerade mitgeteilt, dass diese Methode seit Java 8 zur Standardlibrary gehört:

Code: Alles auswählen

public static boolean isLeap(long year) {
    return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}
Das ist fast identisch mit meinem Code, nur wurde Modulo 4 mit einem bitweisen Und umgesetzt.
Ich bin der Meinung, dass diese Optimierung zugunsten der Rechner so sehr zum Schaden der Programmierer ist, dass man dafuer Schmerzensgeld zahlen sollte. Nicht nur, dass hier ploetzlich das Idiom (4 -- 100 -- 400) verloren gegangen ist, viel schlimmer noch, hier riecht es ja sowas von nach einem Vertipper, dass es IMO einen Kommentar erfordern wuerde, der klarstellt, dass die 3 hier korrekt ist und das keine falsche 4 ist. All dies nur fuer ein kleines bisschen Rechner-Performance ... die der Compiler von morgen sowieso selbststaendig (vielleicht noch viel besser!) wegoptimieren wuerde.

Danke fuer dieses Beispiel als der Java-Standardbibliothek! Das wird mir noch viele Jahre als perfektes Beispiel fuer eine Worst Practice dienen.
Use ed once in a while!

Benutzeravatar
Lord_Carlos
Beiträge: 5578
Registriert: 30.04.2006 17:58:52
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Dänemark

Re: Schaltjahr: "sauberer" Code

Beitrag von Lord_Carlos » 08.11.2016 15:04:02

Meillo hat geschrieben: Ich bin der Meinung, dass diese Optimierung zugunsten der Rechner so sehr zum Schaden der Programmierer ist, dass man dafuer Schmerzensgeld zahlen sollte.
^ Dies.
Die Frage ist nur ob es so Programmiert wurde oder so aus dem de-compiler gekommen ist.

Code: Alles auswählen

╔═╗┬ ┬┌─┐┌┬┐┌─┐┌┬┐╔╦╗
╚═╗└┬┘└─┐ │ ├┤ │││ ║║
╚═╝ ┴ └─┘ ┴ └─┘┴ ┴═╩╝ rockt das Forum!

uname
Beiträge: 12075
Registriert: 03.06.2008 09:33:02

Re: Schaltjahr: "sauberer" Code

Beitrag von uname » 08.11.2016 16:10:11

Also mir gefällt die reine Modulo-Lösung am besten. Auch darf man bei heutigen Programmierern hoffentlich noch etwas IT-Sachverstand voraussetzen. Also die Lösung ist doch aufgrund der Boolesche Algebra sehr leicht zu erklären.

Vielleicht ein etwas einfacheres Beispiel im Vergleich zum Teilproblem: year % 100 != 0 || year % 400 == 0 . Ist aber im Prinzip auch eine Art Implikation als Oder-Funktion.

Bei Debiantmux benötige ich immer genau eine Sitzung. Sollte eine Sitzung existieren, möchte ich sie weiternutzen. Existiert keine Sitzung, so möchte ich eine neue Sitzung aufbauen. tmux bietet mir hierfür keinen eigenen Befehl.

Boolesche Algebra (Implikation)
Wenn ich keine Sitzung weiternutzen kann, dann möchte ich eine Sitzung aufbauen.

a: Sitzung weiternutzen
b: Sitzung aufbauen

Code: Alles auswählen

!a -> b  <=> a v b
Daraus folgt:

Code: Alles auswählen

tmux attach || tmux new
[1] https://de.wikipedia.org/wiki/Boolesche_Funktion

Wie kann ich das nun sauberer, einfacher oder eleganter programmieren? Vorschläge?

Benutzeravatar
Meillo
Moderator
Beiträge: 8818
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Schaltjahr: "sauberer" Code

Beitrag von Meillo » 08.11.2016 16:41:34

uname hat geschrieben: Bei Debiantmux benötige ich immer genau eine Sitzung. Sollte eine Sitzung existieren, möchte ich sie weiternutzen. Existiert keine Sitzung, so möchte ich eine neue Sitzung aufbauen. tmux bietet mir hierfür keinen eigenen Befehl.

[...]

Code: Alles auswählen

tmux attach || tmux new
Ha, da geht's dir wie mir:

Code: Alles auswählen

$ cat ~/bin/t
#!/bin/sh
tmux a || tmux
Die Alternative ist halt, wie du selbst (sinngemaess) schon schreibst:

Code: Alles auswählen

if ! tmux a ; then
    tmux
fi
Eleganter finde ich das nicht und ich kenne auch keine elegantere Umsetzung als mit || ... jedenfalls nicht in den Programmiersprachen, die ich programmiere.
Use ed once in a while!

Antworten