16 Jahre nach dem ersten Check-in ist SQLite die weltweit am weitesten verbreitete Datenbank-Engine. Ein solches Open-Source-Projekt ist eine hervorragende Quelle, um zu lernen, wie man Code leicht verständlich und wartbar gestaltet.
Sehen wir uns einige Fakten zur SQLite-Codebasis an. Beginnen wir dazu mit dem folgenden Codeausschnitt:

Zu dieser Funktion lassen sich einige Beobachtungen machen:
- Die Funktion ist als static deklariert.
- Die Funktion gibt einen Fehlercode zurück.
- Die Funktion hat nur wenige Parameter.
- Die Funktion wird so früh wie möglich verlassen.
- Assertions werden verwendet, um bestimmte Bedingungen zu prüfen.
- Es werden keine globalen Variablen verwendet.
- Die Variablennamen sind leicht verständlich.
- Die Funktion ist kurz.
- Der Funktionskörper enthält keine unnötigen Kommentare.
- Der Funktionskörper ist sauber eingerückt.
Beim Erkunden des SQLite-Quellcodes fällt die Konsistenz der Implementierung auf. Dieselben Best Practices werden auf jede Funktion angewendet.
Hier sind einige Best Practices, die sich aus der SQLite-Codebasis ableiten lassen:
Verwenden Sie Structs zur Speicherung Ihres Datenmodells
In der C-Programmierung verwenden Funktionen Variablen für ihre Verarbeitung. Dabei kann es sich handeln um:
- Statische Variablen.
- Globale Variablen.
- Lokale Variablen
- Variablen aus Structs.
Jedes Projekt besitzt ein Datenmodell, das von vielen Quelldateien verwendet werden kann. Globale Variablen wären eine Möglichkeit, aber keine gute; vorzuziehen sind Structs, um zusammengehörige Daten zu gruppieren.
Let’s search using CQlinq and CppDepend for defined structs:

Zur Beschreibung des Datenmodells werden zahlreiche Structs verwendet.
Halten Sie Funktionen kurz und übersichtlich
Here's, from the Linux coding style web page, some advice about the length of functions:
Functions should be short and sweet, and do just one thing. They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.
The maximum length of a function is inversely proportional to the
complexity and indentation level of that function. So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.Suchen wir nach Funktionen mit weniger als 30 Codezeilen.

Mehr als 90 % der Funktionen haben weniger als 30 Codezeilen.
Kapselung
Kapselung bedeutet, Funktionen und Daten zu verbergen, die intern zu einer Implementierung gehören. In C wird dies mit dem Schlüsselwort static erreicht. Solche Elemente werden als Funktionen und Variablen mit Dateisichtbarkeit bezeichnet.
Suchen wir mit der folgenden CQLinq-Abfrage nach allen statischen Funktionen.

Wie zu sehen ist, sind viele Funktionen als static deklariert.
Anzahl der Funktionsparameter
Funktionen mit NbParameters > 8 können umständlich aufzurufen sein und die Performance beeinträchtigen. Eine Alternative ist eine eigene Struktur zur Übergabe der Argumente.

Nur wenige Funktionen haben mehr als 8 Parameter.
Anzahl lokaler Variablen
Funktionen mit NbVariables > 8 sind schwer zu verstehen und zu warten. Bei mehr als 15 Variablen sind sie extrem komplex und sollten in kleinere Funktionen aufgeteilt werden – außer sie wurden automatisch von einem Tool erzeugt.

Nur wenige Funktionen haben mehr als 15 lokale Variablen.
Vermeiden Sie komplexe Funktionen
Zur Erkennung komplexer Funktionen gibt es zahlreiche Metriken; NBLinesOfCode, die Anzahl der Parameter und die Anzahl lokaler Variablen gehören zu den grundlegenden.
Weitere interessante Metriken zur Erkennung komplexer Funktionen sind:
- Die zyklomatische Komplexität ist eine verbreitete prozedurale Softwaremetrik und entspricht der Anzahl möglicher Entscheidungen innerhalb einer Prozedur.
- Nesting Depth beschreibt die maximale Verschachtelungstiefe eines Bereichs innerhalb eines Funktionskörpers.
- Max Nested Loops entspricht der maximalen Verschachtelungstiefe von Schleifen in einer Funktion.
Welche Höchstwerte für diese Metriken akzeptabel sind, hängt vom jeweiligen Team ab; allgemeingültige Standardwerte gibt es nicht.
Suchen wir nach Funktionen, die Kandidaten für ein Refactoring sind:

Nur sehr wenige Funktionen können als komplex angesehen werden.
Achten Sie auf Const-Correctness
C provides the const-Schlüsselwort ermöglicht es, Objekte, die nicht verändert werden dürfen, als Parameter zu übergeben und anzugeben, dass eine Methode ihr Objekt nicht verändert. Die konsequente Verwendung von const an allen passenden Stellen wird als „const correctness“ bezeichnet. Anfangs ist das gewöhnungsbedürftig, doch const trägt wesentlich zu einem präziseren und robusteren Programmierstil bei.
Suchen wir nach Funktionen mit mindestens einem const-Parameter:

Kopplung von Funktionen
Funktionen, die viele andere Funktionen verwenden, sind schwer zu verstehen und zu warten. Daher sollte die ausgehende Kopplung möglichst gering gehalten werden.
In SQLite weisen nur sehr wenige Funktionen eine hohe ausgehende Kopplung auf:

Verlassen Sie eine Funktion frühzeitig, wenn möglich
Ein frühzeitiges Verlassen einer Funktion, insbesondere durch Guard Clauses am Anfang, ist vorzuziehen, da es die nachfolgende Logik vereinfacht.
Im SQLite-Quellcode wird diese Best Practice bei nahezu allen Funktionen angewendet.
Fazit
Bekannte Open-Source-Projekte zu untersuchen ist immer eine gute Möglichkeit, die eigenen Programmierkenntnisse zu verbessern. Sie müssen das Projekt dafür nicht herunterladen und bauen – Sie können den Code einfach auf GitHub erkunden.
