1. Der Job eines Programmierers

Sehr oft denken unerfahrene Programmierer völlig anders über die Arbeit eines Programmierers als erfahrene Programmierer .

Anfänger sagen oft etwas wie „Das Programm funktioniert, was brauchen Sie sonst noch?“ Ein erfahrener Programmierer weiß, dass „korrekt funktioniert“ nur eine der Anforderungen an ein Programm ist und nicht einmal das Wichtigste !

Lesbarkeit des Codes

Das Wichtigste ist, dass der Programmcode für andere Programmierer verständlich ist . Das ist wichtiger als ein korrekt funktionierendes Programm. Viel mehr.

Wenn Sie ein Programm haben, das nicht richtig funktioniert, können Sie es reparieren. Aber wenn Sie ein Programm haben, dessen Code unverständlich ist, können Sie damit nichts anfangen.

Nehmen Sie einfach ein beliebiges kompiliertes Programm, z. B. Notepad, und ändern Sie die Hintergrundfarbe in Rot. Sie haben ein funktionierendes Programm, aber keinen verständlichen Quellcode: Es ist unmöglich, an einem solchen Programm Änderungen vorzunehmen.

Ein Paradebeispiel dafür ist, dass Microsoft-Entwickler das Pinball-Spiel von Windows entfernten, weil sie es nicht auf die 64-Bit-Architektur portieren konnten. Und sie hatten sogar den Quellcode. Sie konnten einfach nicht verstehen, wie der Code funktionierte .

Abrechnung für jeden Anwendungsfall

Die zweitwichtigste Anforderung an ein Programm besteht darin, jedes Szenario zu berücksichtigen. Oft sind die Dinge etwas komplizierter, als sie scheinen.

So sieht ein unerfahrener Programmierer das Versenden von SMS-Nachrichten:

Ein korrekt funktionierendes Programm

Wie es ein professioneller Programmierer sieht:

Ein korrekt funktionierendes Programm

Das Szenario „funktioniert ordnungsgemäß“ ist normalerweise nur eines von vielen möglichen. Und deshalb beschweren sich viele Neulinge über den Task-Validator von CodeGym: Nur ein Szenario von zehn funktioniert, und der Neuling-Programmierer meint, das sei genug.


2. Ungewöhnliche Situationen

Ungewöhnliche Situationen

Bei der Ausführung eines Programms können ungewöhnliche Situationen auftreten.

Sie möchten beispielsweise eine Datei speichern, es ist jedoch kein Speicherplatz vorhanden. Oder das Programm versucht, Daten in den Speicher zu schreiben, aber der verfügbare Speicher ist knapp. Oder Sie laden ein Bild aus dem Internet herunter, die Verbindung geht jedoch während des Downloadvorgangs verloren.

Für jede abnormale Situation muss der Programmierer (der Autor des Programms) a) sie vorhersehen , b) entscheiden, wie genau das Programm damit umgehen soll , und c) eine Lösung schreiben, die der gewünschten möglichst nahe kommt.

Aus diesem Grund hatten Programme lange Zeit ein sehr einfaches Verhalten: Wenn im Programm ein Fehler auftrat, brach das Programm ab. Und das war ein ziemlich guter Ansatz.

Angenommen, Sie möchten ein Dokument auf der Festplatte speichern. Während des Speichervorgangs stellen Sie fest, dass nicht genügend Speicherplatz vorhanden ist. Welches Verhalten würde Ihnen am besten gefallen:

  • Das Programm wird beendet
  • Das Programm läuft weiter, speichert die Datei jedoch nicht.

Ein unerfahrener Programmierer könnte denken, dass die zweite Option besser ist, da das Programm noch läuft. Aber in Wirklichkeit ist das nicht so.

Stellen Sie sich vor, Sie hätten drei Stunden lang ein Dokument in Word getippt, aber zwei Minuten nach Beginn Ihres Schreibvorgangs wurde klar, dass das Programm das Dokument nicht auf der Festplatte speichern konnte. Ist es besser, zwei Minuten Arbeit oder drei Stunden zu verlieren?

Wenn das Programm nicht tun kann, was es tun soll, ist es besser, es schließen zu lassen, als weiterhin so zu tun, als wäre alles in Ordnung. Das Beste, was ein Programm tun kann, wenn es auf einen Fehler stößt, den es nicht selbst beheben kann, ist, das Problem sofort dem Benutzer zu melden.


3. Hintergrundinformationen zu Ausnahmen

Programme sind nicht die einzigen, die mit ungewöhnlichen Situationen konfrontiert sind. Sie kommen auch innerhalb von Programmen vor – in Methoden. Zum Beispiel:

  • Eine Methode möchte eine Datei auf die Festplatte schreiben, aber es ist kein Platz vorhanden.
  • Eine Methode möchte eine Funktion für eine Variable aufrufen, aber die Variable ist gleich Null.
  • Die Division durch 0 erfolgt in einer Methode.

In diesem Fall könnte die aufrufende Methode möglicherweise die Situation korrigieren (ein alternatives Szenario ausführen), wenn sie weiß, was für ein Problem in der aufgerufenen Methode aufgetreten ist.

Wenn wir versuchen, eine Datei auf der Festplatte zu speichern und eine solche Datei bereits vorhanden ist, können wir den Benutzer einfach bitten, zu bestätigen, dass wir die Datei überschreiben sollen. Wenn kein Speicherplatz verfügbar ist, können wir dem Benutzer eine Meldung anzeigen und ihn auffordern, eine andere Festplatte auszuwählen. Wenn dem Programm jedoch der Speicher ausgeht, stürzt es ab.

Einst haben Programmierer über diese Frage nachgedacht und folgende Lösung gefunden: Alle Methoden/Funktionen müssen einen Fehlercode zurückgeben, der das Ergebnis ihrer Ausführung angibt. Wenn eine Funktion perfekt funktionierte, gab sie 0 zurück . Wenn nicht, wurde ein Fehlercode (nicht Null) zurückgegeben .

Bei diesem Fehleransatz mussten Programmierer nach fast jedem Funktionsaufruf eine Prüfung hinzufügen, um zu sehen, ob die Funktion mit einem Fehler beendet wurde. Der Code wurde immer größer und sah nun so aus:

Code ohne Fehlerbehandlung Code mit Fehlerbehandlung
File file = new File("ca:\\note.txt");
file.writeLine("Text");
file.close();
File file = new File("ca:\\note.txt");
int status = file.writeLine("Text");
if (status == 1)
{
   ...
}
else if (status == 2)
{
   ...
}
status = file.close();
if (status == 3)
{
   ...
}

Darüber hinaus wusste eine Funktion, die einen Fehler entdeckte, häufig nicht, was sie damit anfangen sollte: Der Aufrufer musste den Fehler zurückgeben, und der Aufrufer des Aufrufers gab ihn an seinen Aufrufer zurück und so weiter.

In einem großen Programm ist eine Kette von Dutzenden von Funktionsaufrufen die Norm: Manchmal findet man sogar eine Aufruftiefe von Hunderten von Funktionen. Und jetzt müssen Sie den Fehlercode von ganz unten nach ganz oben übergeben. Und wenn irgendwo unterwegs eine Funktion den Exit-Code nicht verarbeitet, geht der Fehler verloren.

Ein weiterer Nachteil dieses Ansatzes besteht darin, dass Funktionen, die einen Fehlercode zurückgaben, die Ergebnisse ihrer eigenen Arbeit nicht mehr zurückgeben konnten. Das Ergebnis der Berechnungen musste über Referenzparameter übergeben werden. Dies machte den Code noch umständlicher und erhöhte die Fehleranzahl weiter.