Seinen Code ordentlich zu formatieren und sich an diverse Konventionen zu halten hilft erheblich beim Verständnis und dem Schreiben des Codes. Aus diesem Grund werden in InfoA für falsch formatierten Code immer Punkte abgezogen. Dieser Guide listet die wichtigsten Vorgaben auf, an die man sich halten muss.
- Namen
Es gelten folgende Namenskonventionen:
- Variablen:
lowerCamelCase
- Klassen:
UpperCamelCase
- Konstanten:
SCREAMING_SNAKE_CASE
- Methoden:
lowerCamelCase
Alle Namen sollen sinnvoll und beschreibend sein. Ein "berühmter" Witz sagt, dass Namensgebung die schwierigste Aufgabe eines Programmierers ist -- das hat seinen guten Grund. Sich intensiv zu überlegen, wofür eine Variable oder Methode genutzt wird, und erst nach Minuten einen Namen zu vergeben ist oft sinnvoll.
Um auf einen guten Namen zu kommen, kann man sich folgende Frage stellen: "Welche Rolle spielt die Variable in meinem Programm?". Anhand von guten Variablen- und Methodennamen erschließt sich sehr schnell der Sinn dieser Variable/Methode aber auch eines ganzen Codeteils.
Gewissermaßen eine Ausnahme stellt das berühmte i
dar. Es wissen aber alle Programmierer, was eine Variable mit dem Namen i
tut, somit erfüllt der Name wieder seinen Zweck. Allerdings sollte man schon bei verschachtelten Schleifen, die z.B. die Zeilen und Spalten einer Matrix durchlaufen, sinnvolle Namen, wie zeile
und spalte
(oder auch: x
und y
), geben, anstatt auf i
und j
zurück zu greifen.
- Einrückung
Alle sog. "Blöcke" werden eingerückt. Blöcke sind die Rümpfe von if
/else
, for
, while
, do-while
, switch
, Methoden und Klassen. Blöcke sind in der Regel von geschweiften Klammern { }
umschlossen. In der Vorlesung werden einzeilige Blöcke vorgestellt, bei denen man die geschweiften Klammern weglassen kann. Dies ist aber mit zahlreichen Nachteilen verbunden, sodass in Abgaben IMMER Klammern gesetzten werden müssen.
Die öffnende geschweifte Klammer wird immer in die Zeile mit dem Kopf des Blocks geschrieben.
if (a != 0) { // Richtig
// ---------------------------------------
if (a != 0)
{ // Nicht richtig
Der Code des Rumpfes/Blocks startet in der nächsten Zeile um eine Einheit weiter eingerückt. Nach dem Block folgt die schließende geschweifte Klammer wieder eine Einheit weniger eingerückt, also auf der selben Höhe, wie der Kopf.
// Richtig
if (a != 0) {
a = 7;
IO.println("Hallo");
}
// Falsch: Öffnende geschweifte Klammer in neuer Zeile
if (a != 0)
{
a = 7;
IO.println("Hallo");
}
// Falsch: Nicht eingerückt
if (a != 0) {
a = 7;
IO.println("Hallo");
}
// Falsch: Schließende geschweifte Klammer zu weit eingerückt
if (a != 0) {
a = 7;
IO.println("Hallo");
}
Auf eine schließende geschweifte Klammer darf in der selben Zeile nur while (...)
(do-while Schleife), else
oder else if (...)
folgen. Sonst ist }
immer alleine in ihrer Zeile. Also nicht:
if (x < 0) {
x = 3;
} IO.println(x); // Neue Zeile nach `}`!
"Eine Einheit einrücken" bedeutet entweder 2 oder 4 Leerzeichen oder; es muss lediglich konsistent sein und sich nicht in einer Datei abwechseln. vi
rückt standardmäßig mit 2 Leerzeichen ein.
Abgesehen von Blöcken wird nichts eingerückt. Im Folgenden sieht man, wie man es nicht machen darf:
int a = 4;
a++;
int b = 7;
int c = a + b;
IO.println("derp");
Hier wird kein neuer Block angefangen, also gehören alle Anweisungen auf die selbe Einrückungshöhe!
- Spacing
Alle binären Operatoren werden links und rechts von einem Leerzeichen umschlossen. Binäre Operatoren sind:
- Arithmetische (+ Zuweisung):
+ - * / %
(+= -= *= /= %=
) - Vergleichs:
< > == != <= >=
- Logische:
&& || ^
- Bitweise (+ Zuweisung):
& | ^
(&= |= ^=
) - Shift (+ Zuweisung):
<< >> >>>
(<<= >>= >>>=
)
// Richtig
if (n < -1.0 && n > 1.0) {
n += n / 5.0;
}
// Falsch: Alle Leerzeichen vergessen
if (n<-1.0&&n>1.0) {
n+=n/5.0;
}
Alle unären Operatoren werden direkt an den Operanden geschrieben. Unäre Operatoren sind: Logisches Nicht (!
), bitweise Nicht (~
) sowie Inkrement und Dekrement (++ --
).
// Richtig
while (!prim) {
n++;
}
// Falsch: Unnötige Leerzeichen
while (! prim) {
n ++;
}
Außerdem:
- Leerzeichen nach jedem Komma (
IO.print("hi", 4)
und nichtIO.print("hi",4)
) - Leerzeichen um geschweifte Klammern, wenn nötig (
if (x) {
und nichtif (x){
, sowie} else {
und nicht}else{
- Um verschiedene Dinge voneinander zu trennen, immer nur maximal 1 Leerzeichen einsetzen, nicht mehrere.
int x = 3 + 4; // richtig
int x = 3 + 5; // falsch
- Kommentare
Kommentare stehen über dem Code, den sie beschreiben -- nicht darunter. Wenn die Kommentare sehr kurz sind, können sie auch in der selben Zeile stehen, wie der Code, den sie beschreiben. Außerdem sind sie eingerückt, wie normaler Code. Es empfiehlt sich //
Kommentare in Methoden zu verwenden. Nach //
kommt immer ein Leerzeichen, um die Lesbarkeit zu erhöhen.
// ---- Richtig ----
// Alle Zeile der Matrix durchlaufen
for (int zeile = 0; zeile < matrix.length; zeile++) {
// Alle Spalten der Zeile durchlaufen
for (int spalte = 0; spalte < matrix[zeile].length; spalte++) {
// hier mehr Code
}
}
// ---- Falsch ----
for (int zeile = 0; zeile < matrix.length; zeile++) {
//Alle Spalten der Zeile durchlaufen
for (int spalte = 0; spalte < matrix[zeile].length; spalte++) {
// hier mehr Code
}
}
// Alle Zeile der Matrix durchlaufen
Kommentare sollten immer gesetzt werden, um Code zu erklären. Denkt aber dran, nicht zu wiederholen, was der Code eh schon sagt. Es geht nicht darum, jede einzelne Zeile zu erklären, sondern die Funktion und den Sinn eines Codeteils gut zu erklären.
// ---- Schlecht: Sagt nur, was der Code eh schon sagt
// Zahl vom Benutzer einlesen
int n = IO.readInt("Bitte Zahl eingeben: ");
// Variable w deklarieren
int w = 1;
while (w*w < n) {
// w inkrementieren
w++;
}
// Ergebnis ausgeben
IO.println(w);
// ---- Gut: Abstrakt beschreiben, was der Code tut
int n = IO.readInt("Bitte Zahl eingeben: ");
// Berechne die Wurzel von n, indem w solange erhöht wird, bis
// das Quadrat von w größer als n ist. Das Ergebnis ist also die
// ganzzahlige Wurzel + 1
int w = 1;
while (w * w < n) {
w++;
}
IO.println(w);
- Zeichenlimit pro Zeile
Es gibt viele gute Gründe für ein Limit der Zeichen pro Zeile -- unser Totschlagargument ist der Drucker, der die Zeichen nach dem Limit zwangs-umbricht und der Code unlesbar wird. Also müssen alle Abgaben das 80 Zeichen Limit einhalten.
Um lange Zeilen auf mehrere Zeilen aufzuteilen, gibt es mehrere Möglichkeiten. Grundsätzlich gilt: Überall wo ihr ein Leerzeichen macht, könnt ihr auch ein Zeilenumbruch machen (Ausnahme: String Konstantenbezeichner/Literale). Oft macht es auch Sinn komplizierte Rechnungen auf mehrere Zeilen aufzuteilen und Zwischenergebnisse in Variablen zu speichern. Manchmal gibt es auch ebenso sinnvolle, aber kürzere Variablenamen. Hier gibt es keine wirklich feste Regel; man muss selber darauf achten, dass es gut lesbar ist, indem man z.B. entsprechend einrückt. Lange String Literale kann man übrigens mit +
aufsplitten:
// Schlecht
IO.println("Du wartest auf einen Zug, ein Zug der Dich weit weg bringen wird. Du weißt wohin der Zug Dich hoffentlich bringen wird, aber Du weißt es nicht sicher");
// Umgebrochen
IO.println("Du wartest auf einen Zug, ein Zug der Dich weit weg bringen wird. "
+ "Du weißt wohin der Zug Dich hoffentlich bringen wird, "
+ "aber Du weißt es nicht sicher");
- Variablen sollten so spät deklariert werden, wie möglich. Ein Schleifenzähler, der nur in der Schleife gebraucht wird, sollte auch erst dort deklariert werden (
for (int i = 0; i < 3; i++)
stattint i; for (i = 0; i < 3; i++)
). Insbesondere heißt das auch, dass man bloß nicht alle Variablen am Anfang des Programms deklarieren soll -- dies ist lediglich in der Vorlesung an der Tafel sinnvoll. - Eine komplett leere Zeile kann hin und wieder zur Lesbarkeit eingefügt werden
- Wann immer es geht:
for
-Schleife nutzen! Es geht fast immer... - Doppelter Code sollte immer vermieden werden
- Globale Variablen sollten immer vermieden werden
Man kann nicht nur fast immer die for-Schleife statt der while-Schleife nutzen, sondern immer.
z.B. Für einen Boolschen Ausdruck booleanExpression:
Kann man immer statt:
while(booleanExpression) {
//Code
}
Das hier schreiben:
for(;booleanExpression;) {
//Code
}
Für die do-while-Schleife geht es ähnlich wenn man etwas umdenkt:
do {
//Code
} while(booleanExpression)
Ist das gleiche wie:
for(int i = 0; i==1 && booleanExpression;i = 1) {
//Code
}