Training - Beratung - Projektarbeiten

www.David-Tielke.de

C# Beispielprojekt zur CoCo Architektur jetzt auf GitHub

In den letzten drei Ausgaben der dotnetpro durfte ich in der Artikelserie zur "Pragmatischen .NET-Architektur" eine pragmatische Architektur vorstellen, die nicht nur in fast allen Projekten einsetzbar ist, sondern für Entwickler auch besonders einfach zu verstehen und kinderleicht umzusetzen ist. Das im letzten Teil entwickelte Beispielprojekt "KontaktManager" steht jetzt als Projekt auf GitHub zur Verfügung.

Zu dem Thema CoCo wird auf diesem Blog in Kürze eine Webcastreihe erscheinen, die Grundlagen und Praxiseinsatz beleuchten werden.

Links
CoCo KontaktManager C# auf GitHub

Welches Feature ich mir in C# 7.0 wünschen würde

Heute habe ich ein Webinar zu Thema “C# 6.0” bei developer-media durchgeführt, welches in 90 Minuten alle wesentlichen Neuerungen in der sechsten Version, von Microsofts Entwicklungssprache Nr. 1, beleuchtete. Dabei stellte einer der Teilnehmer am Ende eine sehr interessante Frage:

Welches Feature fehlt Dir in C# noch?

Eine interessante Frage wie ich finde, über die ich mir bisher noch nicht so wirklich Gedanken gemacht habe. Aber eigentlich musste ich nicht lange überlegen, denn mit der neuen Sprachversion hat Microsoft fast alles erfüllt, was ich mir gewünscht habe, bis auf einen wichtigen Punkt: Das validieren von Methodenparametern!

Wenn wirklich robuster und qualitativ hochwertiger Code geschrieben werden soll, sollten zu Beginn jeder Methode die Eingangsparameter auf Validität geprüft werden, d.h. Ist er null? Ist der String leer? Ist der Wert in einem gültigen Zahlenbereich?

public Person(string firstname, string lastname, int age)
{
    if (String.IsNullOrWhiteSpace(firstname))
        throw new ArgumentException(nameof(firstname));

    if (String.IsNullOrWhiteSpace(lastname))
        throw new ArgumentException(nameof(lastname));

    if(age < 1 || age > 120)
        throw new ArgumentException(nameof(age));

    // Eigentliche Aufgabe...
}

Ich versuche in allen meinen Projekten peinlichst genau darauf zu achten, dass diese Regel auch eingehalten wird. Dadurch treten Fehler direkt auf und nicht erst einige Module später, bei denen mit dem invaliden Wert gearbeitet wird. Dieser Luxus wird jedoch sehr teuer erkauft, durch sehr sehr komplexen Code. Eine Metrik für einfach zu verstehenden Code ist die zyklomatische Komplexität, welche die Anzahl der Ausführungspfade durch eine Methode angibt. Betrachtet man eine Methode mit 4 Parametern und prüft jeden Parameter nur auf ungleich null, ergibt sich bereits eine Komplexität von 4, ohne das eine Zeile Produktivcode geschrieben wurde. Hat eine Methode einen höheren Wert als 8, so gilt diese als sehr schwierig zu verstehen. Dadurch ergeben sich zahlreiche Nachteile wie schwierige Wartbarkeit, schlechte Verständlichkeit usw.

Darüber hinaus stellt sich mir die Frage, nach Sinn und Zweck, so etwas zu machen. Im Anweisungsblock der Methode sollte die eigentliche Aufgabe der Methode durchgeführt werden und dazu zählt es nicht, ob die Eingangswerte valide sind oder nicht. Genau genommen ist es sogar eine Verletzung des Single-Responsibility-Principles. Was ist also die logische Konsequenz? Das Ganze prüfen, bevor die Methode ausgeführt wird:

public Person(string firstname, string lastname, int age)
	ensure !String.IsNullOrWhiteSpace(firstname) 
			else throw new ArgumentException(nameof(firstname))
	ensure !String.IsNullOrWhiteSpace(lastname) 
			else throw new ArgumentException(nameof(lastname))
	ensure !(age < 1 || age > 120)
			else throw new ArgumentException(nameof(age))
{
	// Eigentliche Aufgabe...
}

Dadurch wird der Code nicht unbedingt lesbarer, aber die Aktivität der Parameterprüfung hat einen fest definierten Ort und der Methodenbody bleibt für die tatsächliche Aufgabe reserviert.

Das Konzept mag etwas an CodeContracts erinnern, in der Tat geht es auch in diese Richtung. Allerdings benötige ich für CodeContracts mindesten das .NET Framework 4.0, da sie nicht Bestandteil der Sprache, sondern des Frameworks sind. Die reine Parameterüberprüfung könnte durch Compiler-Magic in ganz normalem IL-Code resultieren, der sogar abwärtskompatibel wäre. Darüber hinaus werden in C# die CodeContracts in der Methode definiert:

public Rational(int numerator, int denominator) 
{ 
    Contract.Requires( denominator ! = 0 );

    this .numerator = numerator;
    this .denominator = denominator;
}

Eine viel bessere Lösung kommt von Microsoft selbst, in der Forschungssprache Spec# (Die aus C# entstanden ist), wurden die CodeContracts ähnlich umgesetzt, wie ich es mir für C# wünsche. Die Sprache wurde jedoch nur für die Umsetzung des Betriebssystem Singularity erstellt, welches komplett in Managed Code geschrieben wurde:

int ISqrt(int x) 
	requires 0 <= x; 
	ensures result*result <= x && x < (result+1)*(result+1); 
{ 
	int r = 0; 
	while ((r+1)*(r+1) <= x) invariant r*r <= x; 
	{ 
		r++; 
	} 
	return r; 
}

Leider wurde Spec# zusammen mit Singularity eingestellt und die Erkenntnisse davon offensichtlich nicht in C# übernommen.

Auch wenn C#  mit der Version 6.0 fast perfekt wird, würde ich mir eine Kombination aus den CodeContracts aus Spec# und abwärtskompatibler Compiler-Magic für C# 7.0 wünschen!

dotnetpro: Artikel “Besser mit Plan” in der Reihe “Pragmatische .NET Architektur” erschienen.

Heute ist die aktuelle dotnetpro 2/2015 erschienen. Mit dabei auch der erste Artikel von mir in einer dreiteiligen Serie zum Thema “Pragmatische .NET Architektur”. Im ersten Teil “Besser mit Plan”, geht es um die eigentliche Notwendigkeit einer Architektur und was das alles mit Softwarequalität zu tun hat. Dabei wird gezeigt, was ein Qualitätsmodell ist und was es genau auszeichnet. Am Beispiel des ISO/IEC 9126-Modells werden bestimmte qualitative Aspekte ermittelt, welche über verschiedene Projekte konstant sein sollten. Diese Aspekte werden in der anschließend (über die weiteren Teile der Serie) vorgestellten Architektur adressiert. Als erster Schritt wird dazu das zu entwickelnde Softwaresystem schrittweise Modularisiert um so zu einer mehrschichtigen Architektur mit Softwarekomponenten zu kommen. Der von mir auf Fachkonferenzen und UserGroups vorgestellt Ansatz der “Composite Components” ist das Ziel, was am Ende dieser Artikelserie erreicht werden soll.