Ein Mantra für sauberes Softwaredesign
Ohne sauberes Softwaredesign mündet jedes hinreichend lange Softwareprojekt in eine unwartbare Katastrophe. Jetzt, in diesem Moment, werden Unsummen an Geld aufgrund schlechten Softwaredesigns vernichtet. Sei es, weil Abertausende Entwickler Spaghetti-Code in Softwaredesign-Monolithen zu verstehen versuchen, anstatt neue Features zu entwickeln. Sei es, weil Code-Änderungen in Produkten etliche Seiteneffekte nach sich ziehen, oder schlicht und ergreifend, weil Entwickler aufgrund von Stress und Burn-out erkranken oder gar ihren Job an den Nagel hängen. Denn so macht Softwareentwicklung nun wirklich keinen Spaß.
Motivation
Dieser Beitrag soll dem ein Ende bereiten. Es soll eine Art „Mantra“ für sauberes Softwaredesign vorgestellt werden, das leicht zu verinnerlichen ist und auch in stressigsten Projektsituationen Halt gibt. Denn man kann beides haben: Extrem schnelle Feature-Entwicklung und zugleich hohe Qualität auf Softwaredesign-Ebene – eben mit diesem Mantra.
Der Mehrwert dieses Mantras ist, dass es die Anwendung der SOLID-Prinzipien in drei Sätzen ermöglicht. Anstatt das Akronym Buchstabe für Buchstabe und an Beispielen durchzugehen, was in allen von mir gefundenen (Online-)Artikeln/Beiträgen der Fall war, soll dieser Beitrag die Erklärung quasi auf den Kopf stellen: Es werden zunächst die drei Sätze präsentiert, und erst danach wird erklärt, warum dies automatisch zu SOLIDem Softwaredesign führt.
Die nicht gerade bescheidene Hoffnung dahinter ist recht einfach: Dass dieser Artikel für Leserinnen und Leser einen Wendepunkt in ihrem Leben darstellt. Bereitete der Job vor der Lektüre des Textes Magenschmerzen und war der Chef sauer, brennen die Leser:innen danach für ihren Job, stehen morgens voller Elan auf, und auch der Chef ist am Ende glücklich.
Form und Inhalt
Es soll zunächst bei diesem einen Beitrag bleiben. Idealerweise schlägt er wie eine Bombe ein und folgt Voltaires Leitspruch: „Jede Art zu schreiben ist erlaubt, nur nicht die langweilige.“ Daher mag der Artikel durchaus an der ein oder anderen Stelle provokant formuliert sein und als Folge davon möglicherweise als „goldener Hammer“ verunglimpft werden. Aber das ist genau so gewollt: Der Artikel soll so anregend wie möglich geschrieben werden, damit möglichst viele Leserinnen und Leser das Mantra in der Praxis anwenden und beispielsweise feststellen: „In meinem Fall hat es nicht funktioniert!“ Ich bin mir allerdings sicher, dass es in erstaunlich vielen Fällen funktionieren wird.
Zunächst aber kurz zur Definition des Begriffs „Softwaredesign“. Hierzu hat David Tielke bereits alles gesagt, was es zu sagen gibt (Bild 1). Softwaredesign beschäftigt sich mit der Softwarestruktur auf Klassenebene.
Das Ebenen-Modell nach David Tielke (Bild 1)
David TielkeDavids felderprobte Composite-Components-Architektur fußt auf sauberem Softwaredesign, da es eine – wenn auch keine hinreichende, so doch wenigstens notwendige – Bedingung für saubere Softwarearchitektur ist. Daher kann der vorliegende Artikel gewissermaßen als Propädeutik für Davids Composite Components 2.0 gesehen werden.
Das Mantra
Kommen wir nun aber zum eigentlichen Thema, dem Mantra. Ich möchte mit einem Bild starten, da es mehr als tausend Worte sagt.
„UML-Mandalas“ – so entfaltet sich das Mantra in der Praxis (Bild 2)
AutorWas fällt in Bild 2 auf? Richtig, wir haben nur Interfaces mit einer Methode und implementierende Klassen mit einer (public) Methode. Das führt uns auf direktem Wege zu den drei Sätzen des Mantras, die da lauten:
- Nutze Paare an einmethodigen Interfaces und Klassen.
- Lasse alle Klassen auf Interfaces zeigen.
- Füge neue Funktionalitäten durch das Einhängen neuer Paare hinzu.
Wer dieses Mantra beherzigt, ist automatisch SOLID unterwegs:
- Single-Responsibility: Jede Klasse hat eine Hauptaufgabe, vorgegeben durch das implementierte, einmethodige Interface.
- Open-Closed: Wir hängen neue Funktionalitäten durch neue Paare an einmethodigen Interfaces und zugehörigen Klassen ein. Ein Beispiel: Klasse D soll ab sofort irgendetwas validieren, bevor es sein Resultat zurückgibt. Also führen wir Interface E und Klasse E mit jeweils einer (public) Methode ein, welche die Validierung übernimmt, und lassen Klasse D auf Interface E zeigen. Die bestehende Logik von Klasse D wird nicht angefasst, sondern nur um eine weitere Codezeile ergänzt, die die einzige Methode von Interface E aufruft.
- Liskov: Da wir nur gegen Interfaces gehen und von der Vererbung die Finger lassen, können wir nicht böse überrascht werden. Sieht man sich allein die Wikipedia-Beispiele für die Verletzung dieses Prinzips an, wird klar, dass alles Übel daher rührt, dass (ableitende) Klassen von der Implementierung konkreter (Super-)Klassen Gebrauch machen. Aber in unserem Mantra kennt jede Klasse nur Interfaces und kümmert sich ausschließlich um ihren eigenen Kram. Abstrakte Komposition statt konkreter Vererbung.
- Interface Segregation: Geschenkt, denn Interfaces können nicht weniger als eine Methode haben.
- Dependency Inversion: Ebenfalls geschenkt, weil alle Klassen auf Interfaces zeigen.
Das war es auch schon! Jetzt seid ihr an der Reihe – macht euch die Hände schmutzig und wendet das Mantra an! Und sollte die Frage „Wohin mit all den ganzen Interfaces und Klassen?“ aufkommen, empfehle ich die Lektüre der „Composite Components 2.0 Architektur“ in [1] und den nachfolgenden Teilen der dotnetpro-Artikelserie, die vorgibt, wie man die Komponenten (== Assemblies in C#) schneidet, in denen man die Klassen und Interfaces für eine maximal gute Qualität auf Softwarearchitektur-Ebene verstaut.
[1] David Tielke, Was ist Architektur?, dotnetpro 12/2019, Seite 43 ff., www.dotnetpro.de/A1912DDD 8