Wie misst man Clean Code?

Als Software-Entwickler stehe ich am Ende des Tages mit einer mal mehr, mal weniger großen Menge neuen Codes da und stelle mir die Frage: Wie gut ist das, was ich heute geschrieben habe?

Genauso wie viel andere neige ich aber dazu, nicht immer 100 % objektiv zu sein, wenn ich die Qualität meines Codes selber beurteilen muss. Deswegen ist in unserer „Definition of Done“ festgelegt, dass jeder Code durch einen Kollegen revidiert werden muss. Dadurch erhalte ich eine zweite, unabhängigere Sicht auf meinen Code.

Falls man aber gerade keinen Kollegen zur Hand hat, hilft es auch, nacheinander die Prinzipien des Clean Codes durchzugehen und den Code darauf zu überprüfen, wie gut die Prinzipien dort umgesetzt sind. Und um dem eigenen, wohlwollenden Blick ein wenig Objektivität zur Seite zu stellen, hat es sich für mich bewährt, ein paar Code-Metriken zur Unterstützung heranzuziehen.

Bei einer großen Anzahl von zirkulären Abhängigkeiten kann ich mir zum Beispiel nicht mehr vormachen, dass ich das Prinzip der Dependecy Inversion gut umgesetzt habe.
Nicht alle Prinzipien des Clean Codes sind durch Software-Metriken zugänglich, aber für die, bei denen das möglich ist, möchte ich die Metriken dazu vorstellen. Die Clean-Code-Prinzipien, die hier näher betrachtet werden, sind:

  • Keep it Simple, Stupid (KISS)
  • Don’t repeat yourself (DRY)
  • Favour Composition over Inheritance (FCoI)
  • Single Responsibility Principle (SRP)
  • Separation of Concerns (SoC)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)
  • Information Hiding Principle (IHP)
  • Open Closed Principle (OPP).

Um den Artikel kurz zu halten, gehe ich nicht weiter auf die genaue Formulierung der einzelnen Prinzipien ein. Ein Zusammenstellungen der Clean-Code-Prinzipien und -Praktiken sowie deren detailliertere Beschreibung findet man hier: http://www.clean-code-developer.de.
Wichtiger ist mir die Aufzählung von Metriken, und wie ihre Ergebnisse uns helfen, die Umsetzung von Clean-Code-Prinzipien zu messen.

Lines of Code

Zu den einfachsten Metriken gehört die Betrachtung der Codezeilen eines Programms. Die Metrik kann noch ein wenig verfeinert werden, indem man nicht alle Codezeilen betrachtet, sondern die Kommentar-, Leer- und Deklarationszeilen (Methoden, Attribute und Variablen) abzieht, um eine Zahl von Codezeilen zu erhalten, in denen Code steckt, der etwas bewirkt. Früher wurde diese Größen herangezogen, um Wartbarkeit und Testaufwand von Softwareprodukten abzuschätzen. Wenn man diese Metrik auf Klassen- oder Methodenebene anwendet, weist ein kleiner Wert auf eine gute Umsetzung von KISS und SRP hin.

Zyklomatische Komplexität

Die zyklomatische Komplexität ist eine Messgröße für die Anzahl der bedingten Sprunganweisungen im Code. Zunächst wird die einfache Vorschrift festgelegt, die die Maßzahl für die Komplexität von Codeblöcken definiert:

  • Leere Methode = 1
  • Einfache Anweisung = 0
  • Switch-Block = Anzahl der Case Statements
  • Try-catch-Block = Anzahl der Catch Statements
  • Ternärer Operator (Bedingung? Wert1 : Wert2) = 1
  • Logische Verzweigung = Anzahl der && oder || der Bedingung.

Beispielsweise hat folgender Code eine Komplexität von 5:

 

Nach dieser Vorschrift analysiert man den Code, aggregiert die Komplexität auf der Ebene von Methoden, Klassen oder Packages und kann auch Durchschnitts- und Prozentwerte davon bilden.
Ein niedriger Wert der zyklomatischen Komplexität weist auf eine gute Umsetzung der CC-Prinzipien KISS und SoC/SRP hin.

Zirkuläre Abhängigkeiten

Durch Codeanalyse lässt sich die Zahl der zirkulären Abhängigkeiten zwischen Klassen und Packages ermitteln. Eine zirkuläre Abhängigkeit liegt vor, wenn A von B abhängt, B von C und zum Schluss C von A. Die Anzahl der an diesem Kreis beteiligten Objekte wird dabei nicht berücksichtigt. Hier sehe ich nicht die Minimierung der zirkulären Abhängigkeiten, sondern deren Eliminierung als Pflicht des Programmierers. Die Abhängigkeiten verstoßen gegen die CC-Prinzipien DIP und OCP.

Abstraktion und Instabilität (Martin-Metrik)

Für die 1994 von Robert Martin veröffentlichten Metriken zu Abstraktion und Stabilität (http://www.objectmentor.com/resources/articles/oodmetrc.pdf) wird der zur Messung vorliegende Code erst einmal in sog. Softwarekategorien unterteilt. Eine Kategorie ist eine Gruppe zusammenhängender Klassen, die nach Martin folgende Eigenschaften hat:

  1. Eine Klasse innerhalb der Kategorie ist abgeschlossen gegen eine Änderung. Das heißt, wenn die Klasse geändert wird, werden wahrscheinlich auch alle anderen Klassen der Kategorie geändert.
  2. Die Klassen einer Kategorie werden gemeinsam wiederverwendet. Sie sind so stark voneinander abhängig, dass man sie nur schwer zur Wiederverwendung separieren kann und sie besser als Gesamtheit wiederverwendet.
  3. Die Klassen einer Kategorie erreichen zusammen die Realisierung einer gewünschten Funktionalität.

Diese Einteilung zieht die Linie um einen Gruppe von Klassen nach den CC-Prinzipien SoC und IHP: Kategorien können modular (wieder)verwendet werden und verlangen kein tieferes Wissen über ihre konkrete Implementierung.
Die Instabilität einer Kategorie wird dann wie folgt definiert:

 

Softwarekategorien hoher Stabilität (I = 0) sind z.B. Systemkerne oder Datenmodelle.

Softwarekategorien niedriger Stabilität (I = 1) sind z.B. Schnittstellen oder User Interfaces.
Den Abstraktionsgrad einer Softwarekategorie beschreibt Martin so:

 

Für Kategorien mit niedriger Abstraktion wird es schwer, Erweiterungen zu schreiben; Kategorien hoher Abstraktion wiederum zwingen den Entwickler dazu, die verwendete Kategorie zu implementieren, und sind nicht einzeln distribuierbar.
Die beiden Werte zusammen beschreiben ein Quadrat im zweidimensionalen Raum zwischen den Punkten [0,0], [1,0], [1,1], [1,0].

etrachtet man die Punktemenge in diesem Gebiet unter den Aspekten von Clean Code, findet man zwei ideale Zustände:

Der Punkt {0,1}: Abstrakte Kategorien sollen nicht instabil sein. Kategorien, die diesen Wert aufweisen, repräsentieren des Clean-Code-Prinzip OCP: Software soll offen gegenüber Erweiterungen, aber stabil gegen Änderungen des Umfelds sein.

Der Punkt {1,0}: Instabile Kategorien sollen nicht abstrakt sein. Kategorien an diesem Punkt erfüllen das DIP-Prinzip. Pakete mit starker Abhängigkeit sollten keine von ihnen abhängigen Pakete haben.

Die Wertepaare von Instabilität und Abstraktionsgrad auf der Gerade zwischen den beiden Idealen bezeichnet Martin als „ausgeglichen“. Software mit diesen Werten für I und A zeigen eine ideale Balance zwischen diesen beiden Metrik-Aspekten.
Entfernen sich die Werte von I und A von dieser „Main-Sequence“-Richtung vom Ursprung {0,0}, verschlechtert sich die Wartbarkeit der Software (das Reich der Schmerzen); entfernen sich die Werte Richtung {1,1}, verschlechtert sich die Nützlichkeit der Kategorie (das Reich der Nutzlosigkeit).

Mit der Auswertung der Metriken hat man die analysierte Software somit auf die Prinzipien SoC, IHP, OCP und DIP hin gemessen und kann sich nun darüber Gedanken machen, wie die Optimierung von A und I zu besseren Ergebnissen hinsichtlich der Clean-Code-Prinzipien führt. Die Zuordnung der einzelnen Objekte zu den Kategorien und deren Strukturierung in Packages kann so lange variiert werden, bis die Werte für Stabilität und Abstraktion nahe der „Main Sequence“ liegen.

Metriken nach Chidamber und Kemerers

Neben Martin haben auch Chidamber und Kemerers Metriken für objektorientierten Code entwickelt. In ihrem Aufsatz in IEEE Transactions on Software Engineering 20(6), 1994, S. 476–493 haben sie folgende Messgrößen eingeführt:

CBO – (Coupling between Objects)

zählt die Zahl der von einer Klasse abhängigen Klassen plus die Zahl der Klassen, die die Klasse verwenden. (Dieser Wert entspricht dem Ce-Wert der Martin-Metrik.)

DIT – (Depth of Inheritance Tree)

zählt die Vererbungstiefe einer Klasse. Eine zu große Vererbungstiefe widerspricht den CC-Prinzipien FCoI und IHP.

NOC – (Number of Children)

zählt die Anzahl der von einer Klasse erbenden Klassen. Wenn viele Klassen den Code der übergeordneten Klasse nutzen können, entspricht diese Anordnung dem DRY-Prinzip des CC.

RFC – (Response for Class)

zählt alle Methoden einer Klasse plus alle von der Klasse aufgerufenen Methoden abhängiger Klassen. Ein hoher Wert weist auf einen schlachte Umsetzung des SoC-, DIP- und IHP-Prinzips hin.

WMC – (Weighted Methods for Class)

ist die zyklomatische Komplexität einer Klasse (siehe dort).

LCOM – (Lack of Cohesion of Methods)

misst die gemeinsame Nutzung von Attributen einer Klasse durch ihre Methoden.
Wie diese Metriken benutzt werden können, um die Umsetzung der Prinzipien des Clean Code abzuschätzen, ist in der folgenden Tabelle zusammengefasst.

ass einige Aspekte von Clean Code durch die Aussage von mehreren Messwerten gestützt werden, hat den Vorteil, dass man seine Beurteilung nicht auf einzelne Messgrößen stützen muss, sondern eine breitere Basis zur Verfügung hat. Generell wird empfohlen, Codequalität mit mehreren Messgrößen zu bewerten, um Schwächen einer Metrik durch andere Metriken auszugleichen und damit Ergebnisse zu stützen oder relativieren zu können.

Tools

Während die Lines of Code oder die zyklomatische Komplexität von allen gängigen Codeanalyse-Tools wie z.B. Clover oder Sonar gemessen wird, ist die Tool-Unterstützung bei Martin oder Chidamber & Kemerers ein wenig dünner. JDepend ist das bekannteste Tool für die Martin-Metrik; für die von Chidamber & Kemerers findet man hier http://www.spinellis.gr/sw/ckjm/ ein geeignetes Analyse-Werkzeug für Java.

Fazit

Der Einsatz von Software-Metriken in der Entwicklung hilft mir, eine objektivere Sicht auf meinen geschriebenen Code zu erhalten.

Klassen, die in einer zirkulären Abhängigkeit stehen, sollte man sich dringend nochmal ansehen, und Klassen, die einen hohen LCOM-Wert aufweisen, kann man sicherlich nochmal anders kapseln.

Aber man sollte aufpassen sich den Ergebnissen der Metriken zu sehr zu ergeben und Grenzwerte festzulegen und einzuhalten. Denn die Wertigkeit von Code unter den Clean-Code-Aspekten ändert sich nicht zwingend, wenn zum Beispiel der Wert für Lines of Code von 17 auf 21 steigt, obwohl der Grenzwert bei 20 liegt. Oder als anderes Beispiel: Der Wert der zyklomatischen Komplexität lässt sich durch Code-Duplizierung verringern, was dem anderen Clean-Code-Prinzip „Don’t repeat yourself“ widerspricht, das durch die Metriken aber nicht erfasst wird.

Die Ergebnisse der Metriken sollten beim Review nicht die höchste Priorität besitzen, sondern nur als Unterstützung für die Identifikation von Problemzonen des Codes herangezogen werden. Dabei können sie ein hilfreiches Werkzeug sein und haben mich häufig auf Bereiche aufmerksam gemacht, die mir bei meinem persönlichen Review nicht aufgefallen waren.