Die sieben Säulen schönen Codes
Christopher Seiwald
[ English | Deutsch | Français ]
Das Wesentliche schönen Codes ist, dass man viel von der Codestruktur auf einen Blick ableiten kann, ohne ihn vollständig zu lesen. Ich nenne das „visuelle Syntaxanalyse“: den Fluss und die relative Wichtigkeit des Codes über seine Form wahrnehmen. Einen solchen Code zu entwickeln, erfordert eine gewisse Kunstfertigkeit, um einen funktionsfähigen Code in einen funktionsfähigen und lesbaren Code zu transformieren und dabei einen Schritt weiter zu gehen und visuelle Signale für den Benutzer und nicht für den Kompiler zu hinterlassen.
Diese Säulen des schönen Codes greifen auf gewisse Weise in einander. Die ersten fünf sind formelhaft, die letzten beiden intuitiv. Auf alle zusammen wird unter jam/make.c eingegangen. Hierbei handelt es sich um ein C-Beispiel, aber alle diese Vorgehensweisen können für jede Hochprogrammiersprache angewendet werden.
Stilkonform
Code-Änderungen sollten dem Originalstil konform sein. Es sollte nicht möglich sein, vorherige Änderungen in einer Datei wahrzunehmen, ohne die vorherigen Revisionen zu konsultieren. Nichts verdeckt die wichtigen visuellen Signale so sehr wie eine Stiländerung.
Diese Vorgehensweise sollte so umfassend wie möglich angewendet werden: auf jeden Fall innerhalb aller Funktionen, möglichst innerhalb einer Datei und, wenn Sie Glück haben, über das ganze System.
Wenn Sie mit einem wirklich hässlichen oder vernachlässigten Code konfrontiert werden, und Sie können auf den ersten Blick nichts von dem Code ableiten, müssen Sie ihn möglicherweise en gros neu formatieren. Das genaue Verständnis, das Sie dadurch gewinnen, ist dann für alle nachfolgenden Leser verfügbar.
make.c ist in der Revision 27 und wurde kaum umgeschrieben.
Buchformat
Verwenden Sie schmale Spalten. Ähnlich wie bei Büchern und Zeitschriften sollte Code dicht sein, um den Blick zu fokussieren. Wie ich unter „Überwinden der Einrückung“ erwähne, enthält der linke Rand des Codes die Struktur und die rechte Seite die Details. In umfangreichen langen Linien vermischen sich die Bereiche für Struktur und Details und verwirren den Leser.
Es gibt mehrere Möglichkeiten, lange Linien zu vermeiden: Verwenden Sie kürzere Namen (siehe „Aufräumen“); reihen Sie mehrere Funktionsargumente in einer Linie auf (siehe „Gleiche Funktion und gleiches Aussehen“) und rationalisieren Sie die Logik (siehe „Überwinden der Einrückung“).
Als Faustregel gilt: 80 Spalten passen überall, auch wenn es zugegebener Weise praktisch nicht immer möglich ist, einigen Code (wie z.B. breite Tabellen) innerhalb dieser strengen Einschränkungen zu formatieren.
make.c verwendet beides: kurze Variablennamen und eine strenge Einrückung, damit der Code dicht bleibt.
Entwirren von Code-Blöcken
Teilen Sie Code innerhalb der Funktionen in logische Blöcke und entwirren Sie den Zweck separater Blöcke, so dass jeder Block eine Aufgabe oder eine Art von Aufgabe hat. Ein Leser kann das detaillierte Lesen nur dann vermeiden, wenn er durch eine oberflächliche Prüfung die Begebenheit des gesamten Blöcke offen legen kann.
Eine mögliche Ansatzweise besteht darin, dass eine Funktion aus mehreren Mini-Funktionen besteht: jede Mini-Funktion ist ein Block und sollte in sich ganz unabhängig sein. Das heißt, die Informationen, die von einem Block an den anderen weitergegeben werden, sollten genau geprüft werden.
Eine andere Ansatzweise besteht darin, dass eine Funktion aus einem einzigen großen Vorgang besteht. In diesem Fall könnten separate Blöcke entlang der Linien einer Art von Aktivität angeordnet werden: z.B. Initialisieren von Variablen, Prüfen von Parametern, Errechnen von Ergebnissen, Zurücksenden von Ergebnissen und Drucken der Debug-Ausgabe.
Diese Vorgehensweise wird rekursiv für Unterblöcke innerhalb von großen Blöcken angewendet (wie z.B. umfangreiche While-Schleifen).
make.c ist eine Mischform: Ein Block wird für die Fehlerbehebung/Verfolgung abgetrennt und besteht ansonsten aus einer Reihe von Mini-Funktionen, wobei jeder Block einen getrennten Zweck hat.
Kommentieren von Code-Blöcken
Erstellen Sie Code-Blöcke mit Leerzeichen und Anmerkungen, die jeden Block beschreiben. Manchmal enthalten große Blöcke (mit mehrzeiligen Kommentaren) möglicherweise eingebettete kleine Blöcke (mit einzeiligen Anmerkungen).
Anmerkungen sollten beschreiben, was in dem Block passiert und nicht wortwörtlich ins Deutsche übersetzt werden. Falls Ihr Code undurschaubar und Ihre Anmerkungen unklar sind, kann der Leser auf diese Weise zumindest versuchen, den tatsächlichen Zweck zu erkennen.
Umfangreichere Kommentare sind für ausgefeilte oder problematische Code-Blöcke und nicht unbedingt für große Code-Blöcke erforderlich.
Normalerweise hat man 15 % leere und 25 % Zeilen mit Anmerkungen.
make.c enthält so viele Blöcke und Unterblöcke wie für einfache Identifikation erforderlich.
Aufräumen
Reduzieren, reduzieren, reduzieren. Entfernen Sie alles, was den Leser ablenken könnte.
Verwenden Sie kurze Namen (wie i, x) für Variablen mit einem knappen, lokalen Geltungsbereich oder allgemein geläufige Namen. Verwenden Sie Klassenvariablennamen mit mittlerer Länge. Verwenden Sie längere Namen für einen globaleren Geltungsbereich (wie z.B. entfernte oder BS-Schnittstellen). In der Regel gilt: je kleiner der Geltungsbereich, je kürzer der Name. Lange beschreibende Namen helfen dem Leser möglicherweise beim ersten Mal, behindern ihn aber jedes Mal danach.
Entfernen Sie überflüssige Syntaxschnörkel (wie z.B. „!= 0“, unnötiger Zusatz, viele Klammern). Diese Schnörkel sind zwar lehrreich für den unerfahrenen Programmierer, aber Jemand, der ernsthaft Fehler behebt, kann sie nicht gebrauchen und für Jemanden, der versucht, sich ein umfangreiches (oder mittleres) Bild zu verschaffen, sind sie nur im Weg.
Verzichten Sie auf 'ifdef notdef' und jeglichen anderen Dead-Code. Es ist schwierig genug Live-Code zu lesen. SCM-Systeme beinhalten alten Code.
make.c verwendet fast ausschließlich kurze Namen, fast keine syntaktischen Schnörkel und keinerlei Dead-Code.
Gleiche Funktion und gleiches Aussehen
Zwei oder drei Codestücke, die die gleiche oder eine ähnliche Funktion haben, sollten auch gleich aussehen. Nichts beschleunigt das Lesetempo so, wie das Sehen von Mustern.
Zudem sollten diese ähnlich aussehenden Codestücke nacheinander aufgereiht werden. Diese Art von Gruppierung reduziert die Anzahl der Einheiten, die der Leser erfassen muss, ein wichtiger Ansatz um die scheinbare Komplexität des Codes zu vereinfachen.
Diese Vorgehensweise wird am bestem zusammen mit „Entwirren von Code-Blöcken“ verwendet: ein separater Codeblock, der aus einem Linienmuster mit einem einzigen Zweck besteht, ist eine Einheit, die der Leser einfach aufnehmen kann.
Leider muss diese Vorgehensweise durchgehend angewendet werden und erfordert Feinfühligkeit. Zum Glück ist generierter Code nur selten davon betroffen. Einige Beispiele:
- Initialisieren Sie Variablen zusammen.
- Verwenden Sie 'this' einheitlich (oder gar nicht).
- Reihen Sie Parameter bei einem langen Funktionsaufruf aneinander.
- Verwenden Sie {} einheitlich in If-Else-Anweisungen: entweder in allen Blöcken oder in keinem.
- Setzen Sie die Klammer { bei if/for/while-Anweisungen auf die dazugehörige Linie (denn dort befindet sich die schließende Klammer } ).
- Teilen Sie die Verzweigungen bei &&'s oder ||'s und richten Sie sie aus.
Der „4d“-Block von make.c ist ein ausgeklügeltes Beispiel dafür, wie viele Codelinien als eine Einheit dargestellt werden können.
Überwinden der Einrückung
Der linke Rand des Codes gibt die Struktur an, während die rechte Seite die Details enthält. Sie müssen die Einrückung überwinden, um diese Eigenschaft sicherzustellen. Code, der sich zu schnell von links nach rechts bewegt (und wieder zurück), vermischt den wichtigen Steuerungsfluss mit unwichtigen Einzelheiten.
Erzwingen Sie die Anpassung des Hauptsteuerungsflusses auf der linken Seite mit einem Einrückniveau für if/while/for/do/switch-Anweisungen. Verwenden Sie die Anweisungen „break“, „continue“, „return“ und sogar „goto“, um die linksbündige Anordnung zu erzwingen. Ordnen Sie Ihre Verzweigungen erneut, so dass der Block mit dem schnellsten Ausgang als erstes kommt und verwenden Sie anschließend „return“ (oder „break“ bzw. „continue“), so dass der andere Abschnitt auf dem gleichen Einrückungsniveau fortfahren kann.
Echter Code erfordert wichtige Unterblöcke, wenn nötig eingerückt, und diese Unterblöcke müssen dann ebenfalls richtig eingerückt werden. Sie müssen sicherstellen, dass Sie nur Einrücken, um sich von der Struktur zum Detail zu bewegen und nicht aufgrund eines Artefakts der Programmiersprache.
Das ist der schwierigste Teil dieser Vorgehensweisen, denn er erfordert das meiste Geschick und kann häufig die Ausführung individueller Funktionen beeinflussen.
make.c enthält selten mehr als zwei Einrückungslevel und das ist nicht nur zufällig so.
Diese sieben Säulen des schönen Codes sind nicht das letzte und nicht das erste Wort zu gutem Code. Aber was guten Code betrifft, so liegt in ihnen der Unterschied zwischen lesbarem und schönem Code und dem Rest.