Codierungs-Standard
Der hier angeführte
Codierungs-Standard ist im wesentlichen aus dem Buch von Schmaranz (s.
Literaturliste) übernommen. Er ist in einigen wenigen Punkten modifiziert, so
dass er mit den in der Vorlesung verwendeten Beispielen konform ist. Es sind
natürlich auch andere Standards denkbar und sinnvoll, für die Praktikumsaufgaben
und in der Klausur ist jedoch dieser Standard einzuhalten!
1 Generelle Regeln
Die folgenden Prinzipien sind
essentiell für die Lesbarkeit, Wartbarkeit und Erweiterbarkeit eines Programms:
Einfachheit:
Das Prinzip der Einfachheit ist auch als das KISS-Prinzip (Keep It
Small and Simple) bekannt. Kurz gesagt sollen Methoden, Funktionen
und natürlich auch Operatoren genau eine, für ihren Abstraktionslevel adäquate,
atomare Aufgabe erfüllen. Niemals sollen mehrere Aufgaben auf einmal erledigt
werden, genauso wenig, wie Aufgaben erledigt werden sollen, die in ein anderes
Abstraktionslevel gehören (=Durchgriff nach unten oder oben). Parameterlisten
sollen so kurz und übersichtlich wie möglich gehalten werden. Methoden,
Operatoren und Funktionen sollen als Faustregel nicht mehr als 60 Zeilen lang
sein. Eine durchschnittliche Länge von ca. 30 Zeilen ist zu bevorzugen.
Seiteneffekte sind absolut zu vermeiden!
Intuitivität:
Das Prinzip der Intuitivität
bedeutet, dass man den geschriebenen Source-Code "wie ein Buch" lesen und
verstehen können muss, und zwar ohne Kommentare im Source-Code und ohne
Erklärungen des Programmierers! Damit ist impliziert, dass Variablen-, Methoden-
und Funktionsnamen sprechend (=selbsterklärend) und genau ihrer Funktionalität
entsprechend benannt sein müssen. Einbuchstabenvariablen, wie z.B. i, sind nur
in Sonderfällen wie Schleifenzähler erlaubt. Unnötige Kommentare werden als
störend erachtet und sollen dementsprechend weggelassen werden. Ein typisches
Beispiel für solche unnötigen Kommentare wäre:
count++;
// and here the counter is incremented
Einheitlichkeit: Verwandte Teile im Source-Code müssen denselben Prinzipien folgen. Wenn
z.B. eine Funktion
copy
als ersten Parameter den Zielort und als zweiten Parameter den Ursprungsort
nimmt, dann müssen verwandte Funktionen, wie z.B.
move,
sich an dieselben Konventionen halten. Genauso gilt dies auch für Klassen und
ihre Methoden. Nehmen wir z.B. eine Klasse die einen Knoten für z.B. eine
einfach verkettete Liste repräsentiert. Wenn in dieser Klasse der nachfolgende
Knoten über Aufruf von
next()
erreichbar ist, dann darf er nicht in einem Knoten für eine doppelt verkettete
Liste auf einmal über Aufruf von
successor()
erreichbar sein.
2 Codierungs-Regeln
Die hier angeführten Regeln
helfen, den Source-Code so weit wie möglich zu vereinheitlichen und damit die
Arbeit in einem Team zu erleichtern:
-
Die Sprache für Source-Code
ist Englisch. Dies gilt für alle Teile eines Programms,
von Variablennamen über Methoden- und Funktionsnamen bis hin zu Kommentaren im
Source-Code.
-
Der Gebrauch von Block-Kommentaren
(/* comment
*/) ist zu vermeiden.
Stattdessen müssen Zeilenkommentare (// comment) eingesetzt werden. Dies macht Source-Code robuster gegen
Änderungen und erleichtert das Debugging
-
Wenn es sich nicht vermeiden lässt, z.B.
algorithmische Details in Form von Kommentaren in den Source-Code
einzubringen, dann ist ein Block mit einer vollständigen Erklärung des
Algorithmus vor der Implementation des Algorithmus selbst zu schreiben. Es
darf die Kommentierung des Algorithmus nicht im laufenden Source-Code Zeile
für Zeile erfolgen, denn sonst wird der Code durch die Kommentare unleserlich.
Natürlich sind wenige, kurze Kommentare in Form von Cross-References zur
vollständigen Erklärung manchmal sinnvoll und deshalb erlaubt.
-
Sollten jemals Codezeilen in das Programm Eingang
finden, bei denen beim Lesen der Zeile nicht sofort klar ist, was sie tut,
dann muss dies in einem kurzen Kommentar dort festgehalten werden. Jedoch
sollte man sich immer sehr gut überlegen, ob es nicht eigentlich ein besseres,
leichter lesbares Konstrukt gibt, das keinen Kommentar benötigt, um verstanden
zu werden.
-
Globale Variablen sind nach allen Möglichkeiten
zu vermeiden!
-
C-Style Casts sind zu vermeiden. Stattdessen
müssen die "echten" C++-Casts
static_cast,
dynamic_cast, etc. Anwendung finden.
-
Wenn aus irgendwelchen Gründen böse Hacks im
Source-Code temporär nicht vermeidbar sind (z.B. Zeitdruck), so sind diese
unbedingt in hackstart und hack-end Kommentare zu fassen, damit man sie
einfach wieder finden und ausbessern kann. Die hack-... Kommentare haben die
folgendeForm:
//
FIXXME (<author, date>) -> <description of the hack>
[..... the code with the hack .....]
// END FIXXME (<author, date>)
Hier gehört das Keyword
FIXXME immer mit zumindest zwei 'X' geschrieben, denn damit kann man leicht nach ihm
suchen. Je nachdem, wie schlimm der Hack ist, können auch mehrere 'X'
vorkommen. Als Faustregel für die Abstufung gilt, dass der SVH (=Schlimmste
Vorstellbare Hack) mit 5 'X'
geschrieben wird.
-
Klassendeklarationen müssen von ihren
Definitionen getrennt und in einem separaten Header File gespeichert werden.
-
Alle Klassen müssen einen
virtual
Destruktor besitzen.
-
Alle Klassen müssen sowohl den Copy-Constructor
als auch den Zuweisungsoperator explizit implementieren. Wenn keine
Implementation der beiden gewünscht ist, dann müssen sie als Dummies
implementiert und auf
private gesetzt werden.
-
Alle Headers müssen durch Definition und
entsprechende Abfragen von Preprocessor Macros vor Mehrfachinklusion geschützt
werden. Das entsprechende
#define muss der folgenden Namenskonvention entsprechen:
FILENAME_H,
wobei
FILENAME_H
natürlich dem Namen der Headerdatei entspricht.
-
Namen von Bezeichnern
müssen den folgenden Konventionen genügen:
Klassen:
AlleWorteCapitalizedOhneUnderlines
Structures:
GleichWieKlassen
Unions:
GleichWieKlassen
Exceptions:
KlassennameEndetMitException
Methoden und Funktionen:
erstesWortKleinRestCapitalized
Konstanten:
GROSS_MIT_UNDERLINES
Member Variablen:
erstesWortKleinRestCapitalized
Lokale Variablen:
erstesWortKleinRestCapitalized
-
Neben diesen generellen Konventionen gibt es auch
eine spezielle Konvention, die sich auf die Semantik von Methoden bezieht:
Schreibende Zugriffsmethoden auf Member Variablen müssen
setXXX
und ihre lesenden Äquivalente
getXXX
benannt werden (z.B.
setMaxUsers,
getMaxUsers). Eine
Ausnahme dabei bilden Zugriffe auf boolsche Variablen: Hier wird die lesende
Methode semantisch sinnvoll mit
isXXX,
hasXXX,
allowsXXX
o.ä. benannt (z.B.
setReadOnly,
isReadOnly).
-
Die Struktur des Source-Codes muss den folgenden
Prinzipien genügen:
-
Jedes File muss
einen Header besitzen, in dem zumindest der Filename, und eine kurze
Beschreibung des Inhalts zu finden sind. In der Praxis hat es sich eingebürgert,
dass außerdem der Name des Autors, das Erstellungsdatum, das letzte
Änderungsdatum, eine Versionsnummer und ein Copyright-Statement im Header
stehen.
-
Geschwungene
Klammern für Code-Blöcke müssen in einer eigenen Zeile stehen. Ausnahme hiervon
sind Codeblöcke, die zu Kontrollstrukturen (Auswahl, Wiederholung) gehören. Bei
diesen steht die öffnende geschwungene Klammer in derselben Zeile wie das
Schlüsselwort der Kontrollstruktur (if,
while,
for, …). Die Einrückung der Klammern entspricht genau dem
umschließenden Block. Der eingeschlossene Block selbst muss genau um 3 Spaces
eingerückt sein (hier sind in der Praxis Werte zwischen 2 und 4 gängig).
Einrückungen dürfen ausschließlich mit Spaces gemacht werden, Tabs sind
verboten, denn sonst kann es mit verschiedenen Editor-Einstellungen und beim
Drucken Probleme geben.
-
Vor jeder
Operator-, Methoden- oder Funktionsdefinition muss in kurzen Worten beschrieben
werden, wozu diese Funktion dient. Bei größeren Projekten muss auch beschrieben
sein, was bei den einzelnen Parametern erwartet wird, welche Randbedingungen
gelten, welche Return-Values zu erwarten sind und wie diese zu interpretieren
sind.
3 Design Guidelines
Dieser Abschnitt enthält einige
generelle Richtlinien, die helfen sollen, sauberen und robusten Code zu
schreiben.
-
Membervariablen dürfen niemals
public
zugreifbar sein.
-
Interne und externe Zugriffsmethoden dürfen
niemals vermischt werden. Wenn Methoden zum Zugriff für die Außenwelt gedacht
sind und entsprechend
public zugreifbar sind, so dürfen diese klassenintern nicht verwendet
werden. Wenn man beispielsweise ein Paar von Methoden
setVariable,
getVariable als
public
zur Verfügung stellt, dann dürfen diese intern nicht für den Zugriff verwendet
werden. Stattdessen muss der direkte Zugriff auf die Membervariable erfolgen.
Der Grund dafür ist einfach zu erkennen: In
public Methoden werden alle möglichen Schutzmechanismen
eingebaut, die eine Fehlverwendung ausschließen sollen. Man will aber eine
Klasse nicht vor sich selbst schützen. Sollten tatsächlich auch intern
Methoden für gewisse Zugriffe benötigt werden, so sind diese getrennt und
entweder
protected
oder
private
zu implementieren. Durch diese Maßnahme erreicht man eine saubere Trennung
zwischen dem
public
Interface und der internen Implementation einer Klasse.
-
Seiteneffekte und unerwartetes (d.h. nicht aus
dem Namen ersichtliches) Verhalten von Methoden, Operatoren und Funktionen
sind um jeden Preis zu vermeiden!
-
Niemals dürfen sogenannte silent Catches
implementiert werden, also Catches von Exceptions, die ganz einfach sang- und
klanglos ignoriert werden.
-
Exceptions dürfen niemals zu Standard
Flow-Control Zwecken verwendet werden.
-
Abstrakte Methoden in Basisklassen sind unbedingt
solchen mit sinnlosen dummy Implementationen vorzuziehen.