Die POCO C++ Libraries (POCO steht für „Portable Components“) sind eine Sammlung von Open-Source-C++-Klassenbibliotheken, die die Entwicklung netzwerkzentrierter, portabler Anwendungen in C++ vereinfachen und beschleunigen. Die Bibliotheken bieten zahlreiche Funktionen – von HTTP- und HTTPS-Clients und -Servern über XML-Parsing und Datenverschlüsselung bis hin zu Threading-Unterstützung und vielem mehr.
Seit über 15 Jahren nutzen wir die POCO-Bibliothek, um zu überprüfen, ob CppDepend gut implementierte Projekte korrekt bewertet. Diese Einschätzung beruht daher nicht auf einem flüchtigen Eindruck, sondern auf einer gründlichen Analyse zahlreicher POCO-Versionen über einen Zeitraum von 15 Jahren.
Sehen wir uns einen Codeausschnitt aus dem POCO-Quellcode an:
This method implementation is characterized by:
- Sie hat nur wenige Parameter.
- Assertions werden verwendet, um die Gültigkeit der Eingaben zu prüfen.
- The variable names are easy to understand.
- Die Methode ist kurz.
- Der Methodenrumpf enthält keine unnötigen Kommentare; der Code ist selbsterklärend.
- Der Funktionsrumpf ist sauber eingerückt.
- Die STL wird dort eingesetzt, wo es sinnvoll ist.
Beim Eintauchen in den POCO-Quellcode fällt schnell die Konsistenz der Implementierung auf, da für jede Funktion dieselben Best-Practice-Regeln gelten. Bei der Analyse des Quellcodes zeigt sich außerdem, dass POCO selbst für C++-Einsteiger gut zugänglich ist. Konkret weist POCO folgende Merkmale auf:
- Die Bibliothek ist nicht übermäßig komplex gestaltet; fortgeschrittene C++-Kenntnisse sind nicht erforderlich, um ihre Implementierung zu verstehen.
- Die für Anwender bereitgestellten öffentlichen Klassen sind gut organisiert und einfach zu verwenden.
- Das Design ist modular: Die Bibliothek ist in mehrere Projekte unterteilt, die jeweils einen bestimmten Bedarf abdecken.
- Für fortgeschrittene Einsatzszenarien lassen sich bestimmte Klassen bemerkenswert einfach erweitern, anpassen oder in ihrem Verhalten verändern.
In diesem Beitrag konzentrieren wir uns auf einige zentrale Aspekte des Designs:
ABSTRAKTHEIT VS. INSTABILITÄT
Robert C. Martin veröffentlichte einen interessanten Artikel über eine Reihe von Metriken, mit denen sich die Qualität eines objektorientierten Designs anhand der Abhängigkeiten zwischen seinen Subsystemen bewerten lässt.
Darin beschreibt er die Abhängigkeiten zwischen Modulen sinngemäß wie folgt:
Was macht ein Design starr, fragil und schwer wiederverwendbar? Es sind die gegenseitigen Abhängigkeiten der Subsysteme innerhalb dieses Designs. Ein Design ist starr, wenn es sich nicht leicht ändern lässt. Diese Starrheit entsteht dadurch, dass eine einzelne Änderung an stark voneinander abhängiger Software eine Kaskade von Änderungen in abhängigen Modulen auslöst. Wenn Designer oder Maintainer das Ausmaß dieser Änderungskaskade nicht vorhersagen können, lässt sich auch die Auswirkung der Änderung nicht abschätzen. Damit werden auch ihre Kosten unvorhersehbar. Manager zögern angesichts dieser Unvorhersehbarkeit, Änderungen zu genehmigen. So wird das Design starr.
Um dieser Starrheit entgegenzuwirken, führte er Metriken wie Afferent Coupling, Efferent Coupling, Abstractness, Instability, die „Distance from Main Sequence“ sowie das Diagramm „Abstractness vs Instability“ ein.
Das Diagramm „Abstractness vs Instability“ kann dabei helfen, Projekte zu identifizieren, die schwer zu warten und weiterzuentwickeln sind. Hier ist dieses Diagramm für die POCO-Bibliothek:

Die Idee hinter diesem Diagramm lautet: Je stärker ein Codeelement verwendet wird, desto abstrakter sollte es sein. Anders gesagt: Vermeiden Sie starke Abhängigkeiten von konkreten Implementierungen und setzen Sie stattdessen auf Abstraktionen. Mit einem stark verwendeten Codeelement ist hier ein Projekt gemeint, das von vielen anderen Projekten des Programms genutzt wird; das Prinzip gilt ebenso für Packages und Typen.
Es ist keine gute Idee, konkrete Typen zu haben, die in der gesamten Codebasis stark genutzt werden. Dadurch entstehen sogenannte „Zones of Pain“, in denen Änderungen an Implementierungen potenziell große Teile des Programms beeinflussen. Zudem ändern sich Implementierungen erfahrungsgemäß häufiger als Abstraktionen.
Die gestrichelte Main-Sequence-Linie im obigen Diagramm zeigt, wie Abstraktheit und Instabilität ausbalanciert sein sollten. Eine stabile Komponente befindet sich links. An der Main Sequence erkennt man, dass eine solche Komponente sehr abstrakt sein sollte, um nahe an der gewünschten Linie zu liegen. Ist ihr Abstraktionsgrad dagegen niedrig, befindet sie sich in einem Bereich, der als „Zone of Pain“ bezeichnet wird.
Nur das Foundation-Projekt befindet sich in der Zone of Pain. Das ist nachvollziehbar, da es von den anderen Projekten intensiv genutzt wird und hauptsächlich nicht abstrakte Utility-Klassen enthält.
TYPKOHÄSION
Das Single-Responsibility-Prinzip besagt, dass eine Klasse nicht mehr als einen Grund zur Änderung haben sollte. Eine solche Klasse gilt als kohäsiv. Ein hoher LCOM-Wert weist im Allgemeinen auf eine geringe Kohäsion hin. Es gibt mehrere LCOM-Metriken. LCOM nimmt Werte im Bereich [0–1] an. LCOMHS (HS steht für Henderson-Sellers) liegt im Bereich [0–2]. Die LCOMHS-Metrik gilt häufig als wirksamer zur Erkennung nicht kohäsiver Typen. Ein LCOMHS-Wert über 1 sollte als Warnsignal betrachtet werden.


Nur 1 % der Typen gelten als nicht kohäsiv.
In diesem Beitrag haben wir einen kurzen Einblick in das Design von POCO gegeben. In kommenden Beiträgen werden wir Design und Implementierung ausführlicher untersuchen, um zu verstehen, warum POCO zu den besonders gut implementierten Open-Source-C++-Bibliotheken zählt.
