Шукаєте відповіді та рішення тестів для SEW1b 1xHIT Kontrollstrukturen? Перегляньте нашу велику колекцію перевірених відповідей для SEW1b 1xHIT Kontrollstrukturen в elearning.tgm.ac.at.
Отримайте миттєвий доступ до точних відповідей та детальних пояснень для питань вашого курсу. Наша платформа, створена спільнотою, допомагає студентам досягати успіху!
So wie das Thema Einrückungen wurde auch das Thema Namensgebung schon einmal angesprochen. Es ist jedoch so wichtig, dass auch ein Buch zum Thema Clean Code, das für erfahrene Programmierer*innen geschrieben wurde, ein ganzes Kapitel (mit mehreren Seiten) dazu enthält. Schauen wir uns dazu gleich noch einmal das vorherige Beispiel an:
| void main() { int a; int b; int c; int d; a = 1; b = 1; c = 0; d = 0; while(vornFrei() == true) { vor(); a = a + 1; if(kornDa() == false) { b = b + 1; } else { c = c + 1; while(kornDa() == true) { nimm(); d = d + 1; } } } schreib("Körner: " + d + ", Felder gesamt: " + a + ", Felder mit Körnern: " + c + ", leere Felder " + b);} |
Die Variablen in dem Beispiel sind nicht gut benannt. Um zu erkennen, wofür jede Variable steht, muss ich erst den ganzen Code lesen. Erst dann kann ich erraten, was die Variable macht. Je länger und komplizierter ein Programm ist, desto schwieriger ist diese Analyse. Ein guter Name beschreibt, was in der Variablen gespeichert ist bzw. was der Sinn und Zweck dieser Variablen ist. Man kann daraus gleich die Frage nach dem "Warum brauche ich diese Variable?" beantworten (Psst ... diese gemeine Frage soll hin und wieder auch bei Abgabegesprächen gerstellt worden sein). Durch die schlechte Wahl der Namen ist das Programm auch bei der Ausgabe sehr fehleranfällig. Ob wirklich der richtige Wert in der richtigen Variablen steht, ist bei der schreib
-Anweisung nicht zu erkennen.
Dazu musst du verstanden haben, wofür du die Variable brauchst. Die Variable d
z.B. zählt wieviele Körner der Hamster aufgesammelt hat. Das heißt es ist immer die Anzahl an Körnern, die der Hamster gerade hat, gespeichert. Also wären z.B. koernerZaehler
oder anzahlKoerner
gute Namen. Bei den Namen sind neben der Bedeutung auch noch folgende Faktoren zusätzliche Faktoren wichtig:
Vergleichen wir also das Programm mit einer besseren Variablenbenennung:
| void main() { int anzahlFelderGesamt; int anzahlLeereFelder; int anzahlFelderMitKorn; int anzahlKoerner; anzahlFelderGesamt = 1; anzahlLeereFelder = 1; anzahlFelderMitKorn = 0; anzahlKoerner = 0; while(vornFrei() == true) { vor(); anzahlFelderGesamt = anzahlFelderGesamt + 1; if(kornDa() == false) { anzahlLeereFelder = anzahlLeereFelder + 1; } else { anzahlFelderMitKorn = anzahlFelderMitKorn + 1; while(kornDa() == true) { nimm(); anzahlKoerner = anzahlKoerner + 1; } } } schreib("Körner: " + anzahlKoerner + ", Felder gesamt: " + anzahlFelderGesamt + ", Felder mit Körnern: " + anzahlFelderMitKorn + ", leere Felder " + anzahlLeereFelder);} |
Manche sagen vielleicht: "Puh das sind jetzt viele Buchstaben und das macht das ganze noch schwerer zu lesen". Tatsächlich werden wir in den nächsten Kapiteln die Anzahl der Buchstaben wieder reduzieren. Der Vorteil von diesem Ansatz ist jedoch, dass sofort sichtbar ist, was jede Variable macht. Und gute Variablenbezeichnungen helfen uns auch dabei, den Code besser zu verstehen. Durch die Variablennamen kann man ohne viel Nachdenken erkennen, dass die if
-else
-Verzweigung zur Unterscheidung von leeren Feldern und Feldern mit Körnern dient. Auch bei der Ausgabe-Anweisung ist man besser gegen Fehler geschützt, weil erkennbar ist, welche Variable zu welchem Beschreibungstext gehört.
Eine gute Namensgebung bei Variablen ist aus folgenden Gründen wünschenswert:
Welche der folgenden Variablennamen sind nach den Syntaxregeln ungültig?
Welche der folgenden Variablennamen sind nach den Syntaxregeln und der Namenskonvention in Java ungültig?
Achtung umgekehrte Fragestellung:Welche der folgenden Variablennamen sind nach den Syntaxregeln und der Namenskonvention in Java gültig und sind gleichzeitig aussagekräftige und damit gute Variablennamen?
Der Autor eines der bekanntesten Bücher über Clean Code schreibt dazu:
Comments do not make up for bad code - Robert C. Martin (Kommentare machen schlechten Code nicht wett)
Kommentare haben ihren Zweck und ihre Aufgabe. Dazu muss man unterscheiden, um welche Kommentare es sich handelt:
Dokumentationskommentare beschreiben nicht den Code. Sie liefern eine Zusammenfassung eines Programms (oder später auch einer Methode). Sie sollen verstanden werden ohne den Code zu sehen. Sie bilden für zukünftige Programmiererinnen und Anwenderinnen eine Entscheidungshilfe, ob das Programm (oder die Methode) verwendet werden soll. Durch die Angabe von Autor und Datum bekommt man zusätzliche Informationen über die Verantwortlichkeit und ob man überhaupt die richtige Version verwendet.
Manchmal werden für die Initialisierung nicht offensichtliche Werte verwendet. Warum z.B. werden die ersten 2 Variablen in unserem Beispiel-Programm mit 1 initialisiert? Dies sollte jedenfalls durch Kommentare erklärt werden. Dadurch können auch Hinweise auf Schwächen im Programm gegeben werden. Dass wir die Anzahl der leeren Felder gleich einmal mit 1 annehmen und davon ausgehen, dass auf dem ersten Feld sowieso keine Körner liegen, ist z.B. nicht optimal und sollte in einer Überarbeitung verbessert werden.
Meistens entstehen Programmcodes über einen längeren Zeitraum. Manchmal kann man sich auch nicht gleich um alle Probleme gleichzeitig kümmern, sondern man muss Prioritäten setzen. Deshalb ist es empfehlenswert, offene Punkte mit einem TODO (abgekürzt aus dem englischen there is something to do) versehen. Viele Programmieroberflächen unterstützen diese Kommentare und heben sie gesondert hervor. In einer endgültigen Abgabe sollten sie natürlich nicht mehr vorkommen.
Auch ein normaler Text profitiert von Absätzen und Überschriften. Er verleiht einem Text Struktur und man kann ihn dadurch leichter lesen. Mit Leerzeilen und Zeilenkommentaren kann man dies auch für Code erreichen. Nach einer Reihe von zusammengehörenden Anweisungen, wie z.B. den Variablen-Deklarationen und -Initialisierungen kann man durch eine leere Zeile zeigen, dass man zu einem neuen Teil des Programmcodes übergeht. Verstärken kann man dies noch, in dem man diesem neuen Code-Teil eine Art "Überschrift" gibt, die beschreibt, was im nächsten Programmteil zu erwarten ist. Dadurch bekommt das Programm zwar insgesamt mehr Zeilen, aber auch mehr Struktur. Diese Struktur ist für einen menschlichen Leser leichter zu erfassen.
Der folgende Programmcode zeigt alle besprochenen Punkte beispielhaft auf:
|
|
Kreuze an, welche Aufgaben ein Dokumentationskommentar hat:
Kreuze an, welche Aufgaben Zeilen- (oder Blockkommentare) haben:
Oft werden die gleichen Code-Zeilen an verschiedenen Stellen im Programm benötigt. Als Programmier-Anfängerin ist man versucht dieses Problem mit "Copy und Paste" (Kopieren und Einfügen) zu lösen. Und tatsächlich hat man als Programmier-Anfängerin noch nicht alle Werkzeuge, um Code-Verdopplung ganz vermeiden zu können. Ein weiteres Werkzeug dafür wird z.B. im folgenden Modul 1.c Methoden vorgestellt.
Trotzdem kann man sich schon jetzt bemühen, Code-Verdopplungen zu vermeiden. Sieh dazu folgenden Programmteil aus dem Beispiel an. Dieses Programmstück ist von der Funktion her gleich mit dem enstprechenden Teil vom vorgestellten Programm. Trotzdem hat es einen Nachteil: es kommt doppelter Code vor.
Grundsätzlich funktioniert das Programm mit obigen Zeilen nicht schlechter als die ursprüngliche Version. Das Problem ergibt sich (wie bei dem ganzen Kapitel) aus der Lebensdauer und den folgenden Änderungen oder Verbesserungen. Wenn wir das Programm z.B. so umändern, dass wir nicht mehr inkrementieren, sondern dekrementieren müssten, dann muss ich das an **mehreren Stellen* ändern. Dabei kann es leicht passieren, dass man eine Änderung vergisst und damit hat man sich wieder einen Fehler im Programm eingebrockt.
Außerdem verschlechtert Code-Verdopplung die Lesbarkeit, weil Abläufe unnötig verkompliziert werden und die einzelnen Teile unübersichtlich viele Code-Zeilen enthalten. Die ursprünglich präsentierte Version ist vom Ablauf her klarer, als die Version mit den doppelten Codezeilen:
Bis jetzt wurden Inkrement und Dekrement-Operatoren nur in den EKs besprochen. Diese Operatoren sind speziell dafür da, den Wert einer Variablen um 1 zu erhöhen oder um 1 zu verringern. Das Erhöhen um 1 nennt man inkrementieren. Das Verringern um 1 wird als dekrementieren bezeichnet.
Um den Wert einer Variable namens schritteZaehler
um 1 zu erhöhen kann man entweder
schritteZaehler = schritteZaehler + 1;
schreiben, oder man verwendet den Inkrement-Operator und schreibt einfach
schritteZaehler++;
Die erste Variante bietet mehr Flexibilität, weil die Anweisung leicht abänderbar ist: z.B. den Wert immer um 2 erhöhen, oder den Wert verdoppeln. Die zweite Variante kann nur den Wert einer Variablen (schritteZaehler
in diesem Fall) um 1 erhöhen. Das kann sie richtig gut und diese Anweisung wird oft genug benötigt, dass sich ein eigener Operator auszahlt.
Fun fact: die ebenfalls sehr wichtige und populäre Programmiersprache C++ heißt deshalb so, weil sie eine Erweiterung der Programmiersprache C ist - also C inkrementiert. Java gehört zu den C-ähnlichen Sprachen und hat diese Syntax von C übernommen (wie viele andere Befehle auch).
Das Gegenteil von inkrementieren ist dekrementieren, also Werte immer um 1 verringern. Standardmäßig kann man das mit einer bereits deklarierten und initialiserten Variable koernerImMaul
folgendermaßen tun:
koernerImMaul = koernerImMaul - 1;
Oder man verwendet den Dekrement-Operator und schreibt einfach
koernerImMaul--;
Unser Programm wird durch die Verwendung des Inkrement-Operators noch übersichtlicher (sofern man weiß, was dieser Operator macht).
| void main() { int anzahlFelderGesamt = 1; int anzahlLeereFelder = 1; int anzahlFelderMitKorn = 0; int anzahlKoerner = 0; while( vornFrei() ) { vor(); anzahlFelderGesamt++; if( !kornDa() ) { anzahlLeereFelder++; } else { anzahlFelderMitKorn++; while( kornDa() ) { nimm(); anzahlKoerner++; } } } schreib("Körner: " + anzahlKoerner + ", Felder gesamt: " + anzahlFelderGesamt + ", Felder mit Körnern: " + anzahlFelderMitKorn + ", leere Felder " + anzahlLeereFelder);} |
Einen Wert um 1 erhöhen nennt man in der Fachsprache .
Einen Wert um 1 verringern nennt man in der Fachsprache .
Die bereits deklarierte und initialisierte Variable schritteZuGehen
soll um 1 verringert werden. Schreibe die entsprechende Anweisung mit Hilfe eines Inkrement- oder Dekrement-Operators:
Die bereits deklarierte und initialisierte Variable koernerAmFeld
soll um 1 erhöht werden. Schreibe die entsprechende Anweisung mit Hilfe eines Inkrement- oder Dekrement-Operators:
boolean
-Werten ohne VergleichJede Bedingung fordert einen boolean
-Wert: ist die Bedingung erfüllt - true
- oder ist sie nicht erfüllt - false
. Dieser boolean
-Wert wird meistens über einen Vergleich erzeugt. Ein Vergleich ist also der "Standard-Fall", wie eine Bedingung formuliert wird.
Wie zuvor festgestellt, hat also das Ergebnis eines Vergleichs den Typ boolean
. Wenn wir aber bereits über eine Methode oder in einer Variablen einen boolean
-Wert haben, dann kann dieser Wert auch ohne Vergleich direkt in einer Bedingung verwendet werden. Achtung: das funktioniert nur mit dem Datentyp boolean
. Die "Sinnesmethoden" des Hamsters liefern z.B. true
oder false
und damit genau so einen boolean
-Wert. Deshalb können sie auch ohne Vergleich direkt als Bedingung verwendet werden.
Wenn die Namen gut gewählt sind, erhöht dies die Lesbarkeit vom Code, da er direkt aus dem "Programmier-Englischen" übersetzt werden kann:
Solange vorn frei (ist)...
Auf solche boolean-Methoden können auch direk logische Operatoren angewendet werden. Insbesondere der NICHT-Operator wird in diesem Zusammenhang oft verwendet:
Solange NICHT Maul leer (ist)...
Auf unser Beispiel angewandt, sieht das so aus:
| void main() { int anzahlFelderGesamt = 1; int anzahlLeereFelder = 1; int anzahlFelderMitKorn = 0; int anzahlKoerner = 0; while( vornFrei() ) { vor(); anzahlFelderGesamt = anzahlFelderGesamt + 1; if( !kornDa() ) { anzahlLeereFelder = anzahlLeereFelder + 1; } else { anzahlFelderMitKorn = anzahlFelderMitKorn + 1; while( kornDa() ) { nimm(); anzahlKoerner = anzahlKoerner + 1; } } } schreib("Körner: " + anzahlKoerner + ", Felder gesamt: " + anzahlFelderGesamt + ", Felder mit Körnern: " + anzahlFelderMitKorn + ", leere Felder " + anzahlLeereFelder);} |
Schreibe eine Kopfzeile, die folgendes aussagt Solange Korn da (ist) ...:
Schreibe eine Kopfzeile, die folgendes aussagt Wenn NICHT vorn frei (ist) ...:
Um die Begriffe Deklaration und Initialisierung gut zu beschreiben und sie auch nicht zu verwechseln, haben wir bisher immer die Deklaration einer in einer eigenen Zeile geschrieben und die Initialisierung der Variablen in einer zweigen eigenen Zeile. Zur Erinnerung:
Diese ist unbedingt notwendig, weil eine Variable nach ihrer Deklaration noch keinen definierten Wert hat:
Man kann auch beide Schritte in einer Anweisung durchführen. Trotzdem wird zuerst die Deklaration und dann die Initialisierung durchgeführt:
Dadurch kann man den Anfangswert, den eine Variable bekommt oft noch leichter erkennen, und das Programm wird übersichtlicher, weil unnötige Zeilen wegfallen:
| void main() { int anzahlFelderGesamt = 1; int anzahlLeereFelder = 1; int anzahlFelderMitKorn = 0; int anzahlKoerner = 0; while(vornFrei() == true) { vor(); anzahlFelderGesamt = anzahlFelderGesamt + 1; if(kornDa() == false) { anzahlLeereFelder = anzahlLeereFelder + 1; } else { anzahlFelderMitKorn = anzahlFelderMitKorn + 1; while(kornDa() == true) { nimm(); anzahlKoerner = anzahlKoerner + 1; } } } schreib("Körner: " + anzahlKoerner + ", Felder gesamt: " + anzahlFelderGesamt + ", Felder mit Körnern: " + anzahlFelderMitKorn + ", leere Felder " + anzahlLeereFelder);} |
Achte aber auch immer darauf, die einzelnen Vorgänge (Deklaration und Initialisierung) nicht zu vermischen. Manchmal wird auch nur eine Deklaration oder zu einem anderen Zeitpunkt dann nur eine Initialisierung benötigt.
Deklariere eine Variable für Dezimalzahlen (mit Nachkommastellen) mit dem Namen produktPreis
:
Initialisiere die bereits deklarierte Variable produktPreis
mit dem Wert 17,9:
Deklariere eine Variable für Dezimalzahlen (mit Nachkommastellen) mit dem Namen produktPreis
und initialisiere sie gleich mit dem Wert 17,9:
Initialisiere die bereits deklarierte Variable namens satzzeichen
mit dem Symbol !
:
Deklariere eine Variable für Wahrheitswerte namens istErledigt
und initialisiere sie mit dem Wert für falsch:
Deklariere eine Variable für Texte mit dem Namen eingabeVorname
:
Clean Code ist ein Prinzip, nachdem Code grundsätzlich gestaltet werden soll. In der Schule werden Code-Beispiele oft für eine Abgabe geschrieben und dann wieder vergessen. In der Praxis kann ein Programmcode über Jahre hinweg immer wieder wichtig sein. Dieser Programmcode wird oft auch von mehreren Personen bearbeitet und angepasst, weil der/die ursprüngliche Programmierer*in schon längst auf einem anderen Posten in der Firma sitzt. Daher ist es wichtig, dass Code gut für andere Menschen lesbar und verständlich ist. Die folgenden Abschnitte weisen noch einmal auf ein paar diesbezügliche Punkte hin, die schon besprochen wurden. Außerdem werden ein paar Punkte thematisiert, die bis jetzt verwendet wurden, weil sie für das Lernen und Erklären einfacher waren, aber dem Clean Code - Prinzip wiedersprochen haben. Das Thema wird auch immer wieder in regelmäßigen Abständen aufgegriffen werden und um neue Aspekte erweitert werden.
Einer der wichtigsten Punkte, um Code lesbar zu gestalten, liegt in den Einrückungen. Durch sie werden Programmstruktur und Ablauf besser sichtbar. Gleichzeitig ist es auch wichtig, dass Zeilen nicht zu lange werden. Lange Zeilen können von unseren Augen nicht so gut auf einen Blick erfasst werden. Es gibt verschiedene Quellen, aber die meisten schlagen einen Bereich von 50-70 Zeichen vor, manche gehen bis maximal 80 Zeichen.
Seht einmal den folgenden Code an
| void main() { int a; int b; int c; int d; a = 1; b = 1; c = 0; d = 0;while(vornFrei() == true) { vor(); a = a + 1; if(kornDa() == false) {b = b + 1; } else { c = c + 1; while(kornDa() == true) { nimm(); d = d + 1;}}} schreib("Körner: " + d + ", Felder gesamt: " + a + ", Felder mit Körnern: " + c + ", leere Felder " + b);} |
Was dieser Code macht, kann man vielleicht noch an der Ausgabe-Anweisung erraten, aber es wird sehr schwer, etwas auszubessern, Fehler zu finden oder zu ergänzen. Durch Einrückungen erst wird der Code übersichtlicher, man kann Strukturen und Verschachtelungen erkennen und dadurch leichter mit diesem Programm weiter arbeiten. Außerdem wurde die lange Ausgabezeile bei einem Verkettungsoperator (+) in die nächste Zeile umgebrochen, um besonders lange Zeilen zu vermeiden.
| void main() { int a; int b; int c; int d; a = 1; b = 1; c = 0; d = 0; while(vornFrei() == true) { vor(); a = a + 1; if(kornDa() == false) { b = b + 1; } else { c = c + 1; while(kornDa() == true) { nimm(); d = d + 1; } } } schreib("Körner: " + d + ", Felder gesamt: " + a + ", Felder mit Körnern: " + c + ", leere Felder " + b);} |
Die Standard-Größe bei Einrückungen ist 4 Leerzeichen bzw. 1 Tabulatur und sollte auch nicht verändert werden. Ausnahmen kann man machen, wenn man z.B. für einen Ausdruck nicht so viel Platz für eine Zeile hat.
Die Einrückungsregeln sind wie folgt:
Clean Code beschäftigt sich mit dem Thema, wie Quellcode
Dieses Thema ist wichtig, weil in der Arbeitswelt Quellcode
Einrückungen helfen in diesen Bereichen, weil ... (kreuze die zutreffenden Antworten an, mehrere Antworten möglich)
Die Kopfzeile der main-Methode im Hamster ist immer um
Die Anweisung in Programmzeile 2 steht nach 1 geschwungen öffnenden Klammer. Daher ist sie um Leerzeichen eingerückt, genauso wie die nächsten Anweisungen in den Programmzeilen 3-9. Diese Anweisungen befinden sich also alle auf einer Ebene.
Erst am Ende der Kopfzeile der while-Schleife in Zeile 10 wird eine geschwungende Klammer aufgemacht. Daher ist diese Kopfzeile noch genau Leerzeichen eingerückt.
In der nächsten Anweisung in Zeile 11 sind schon 2 offene geschwungene Klammern wirksam (die von Zeile 1 und von Zeile 10). Deshalb ist diese Zeile schon um Leerzeichen eingerückt. Das gilt genauso für die Anweisung in Zeile 12.
Erst am Ende der Kopfzeile der if-Verzweigung in Zeile 13 wird eine geschwungende Klammer aufgemacht. Daher ist diese Kopfzeile noch genau Leerzeichen eingerückt.
Danach sind für die Anweisung in Zeile 14 bereits 3 offene geschwungene Klammern wirksam (die von Zeile 1, von Zeile 10 und von Zeile 13). Deshalb ist diese Zeile schon um Leerzeichen eingerückt.
In Zeile 15 wird der Block, der in Zeile 13 mit der if-Verzweigung aufgemacht wurde, wieder geschlossen. Daher ist er genauso weit eingerückt, wie die Kopfzeile dieser if-Anweisung nämlich Leerzeichen.
Um schließende geschwungene Klammern gut zuordnen zu könnnen, muss man sich einfach eine Linie senkrecht nach oben von der Klammer denken (wenn man die Einrückungen richtig gemacht hat). Die nächste Zeile, auf die man von unten nach oben stößt, enthält die zugehörige öffnende Klammer. So ist z.B. die Klammer in Zeile 22 um Leerzeichen eingerückt. Wenn man von dieser Zeile nach oben geht, ist die nächste Anweisung, auf die man mit 8 Leerzeichen Einrückung stößt, die Anweisung, die in der Zeile mit der Nummer steht. Daraus sieht man, dass genau dieser Block geschlossen wird.
Die schreib
-Anweisung in Zeile 24 ist auf 2 Teile aufgeteilt. Der erste Teil in Zeile 24 ist um 4 Leerzeichen eingerückt. Deshalb ist der zweite Teil der schreib
-Anweisung in Zeile 25 um Leerzeichen eingerückt.
Ist folgender Ausdruck eine gültige Bedingung (d.h. sie lässt sich in Java compilieren)? Die Variable zaehler wurde zuvor deklariert und initialisiert.
if( !(zaehler == 0 || zaehler > 0) ) {
Wie oft wird der Schleifenkörper zu folgender Kopfzeile ausgeführt (zaehler wurde zuvor als int deklariert):
for(zaehler = 0; zaehler != 10; zaehler = zaehler + 2) {
Ist folgender Ausdruck eine gültige Bedingung (d.h. sie lässt sich in Java compilieren)? Die Variable zaehler wurde zuvor deklariert und initialisiert.
if(zaehler < 0 || 0 ) {
Отримайте необмежений доступ до відповідей на екзаменаційні питання - встановіть розширення Crowdly зараз!