Menschen von oben fotografiert, die an einem Tisch sitzen.

adesso Blog

Selbst wenn im Anforderungsmanagement nichtfunktionale Anforderungen berücksichtigt werden, stellen Wartbarkeit und Robustheit selten zentrale Aspekte dar, die auch konsequent umgesetzt werden. Sie können schließlich nur schwer getestet und gemessen werden und tauchen diesbezügliche Probleme auf, werden diese als technische Schulden erst sehr viel später sichtbar. Nämlich dann,

  • wenn selbst kleine Erweiterungen immer aufwendiger werden oder
  • wenn Fehler, die in keinem Zusammenhang mit Änderungen stehen, an ganz anderen Stellen der Software entstehen.

Die Verantwortung für gute Softwarearchitektur trägt nicht der Kunde, sondern die Softwareentwickler.

Die Grundlage von Clean Code

Eins vorweg: Softwarearchitektur sollte eher von Prinzipien als von starren Regeln geleitet sein. Solche Prinzipien geben bei der Entwicklung den Rahmen vor und sind sozusagen die Leitplanken, an denen die Struktur der Software konzipiert werden sollte. Da die konkrete Umsetzung von Software durch die Entwicklerinnen und Entwickler im Team erfolgt, führt dies auch zu einer starken Identifikation mit der entwickelten Architektur. Dabei sind alle Software Developer und Software Architects gefordert, sich intensiv mit der jeweiligen Architektur auseinanderzusetzen und die beste oder zumindest passendste Architektur zu finden.

Wie ihr wisst, kann nicht immer jede Entscheidung basisdemokratisch getroffen werden. Es sollten sich trotzdem die im Team anerkannten Werte und Prinzipien in der Entscheidung wiederfinden. Dies erhöht in jedem Fall mehr die Akzeptanz einer Architektur, als wenn sie als starre Vorgabe von außen in den Entwicklungsprozess eingebracht wird.

Als Ausgangspunkt für eine gute Softwarearchitektur haben sich die SOLID-Prinzipien des amerikanischen Softwareentwicklers und IT-Beraters Robert C. Martin bewährt. Sie bilden den Kern einer ganzen Reihe von Prinzipien. Was sich hinter dem Akronym „SOLID“ verbirgt, ist recht simpel und schnell erklärt:

  • Single-Responsibility-Prinzip
  • Open-Closed-Prinzip
  • Liskovsches Substitutionsprinzip
  • Interface-Segregation-Prinzip
  • Dependency-Inversion-Prinzip

Da uns diese fünf SOLID-Prinzipien eine wichtige Hilfestellungen bei der Entwicklung von Software geben und zu einem guten objektorientierten Design führen sollen, möchte ich euch die einzelnen Begriffe näher erklären.

Single-Responsibility-Prinzip

Dieses Prinzip besagt, dass jede Komponente nur für eine Aufgabe verantwortlich sein und auch die gesamte Funktionalität einer Aufgabe abdecken sollte. Werden Änderungen an der Software vorgenommen, sollte es immer einen Grund dafür geben. In der Praxis hat eine Komponente dann die richtige Größe, wenn sich die Änderungen aufgrund einer User Story nur auf eine Komponente auswirken.

Open-Closed-Prinzip

Das Open-Closed-Prinzip lässt sich sowohl im kleinen sowie großen Rahmen anwenden. Erweiterungen an Klassen sollten nicht durch Änderung der Klasse durchgeführt, sondern über Vererbung oder besser durch Delegation gelöst werden. Bei größeren Softwareeinheiten ist ein Plugin-Konzept sinnvoll, über das zusätzliche Klassen zur Erweiterung der Funktionalität hinzugefügt werden können.

Liskovsches Substitutionsprinzip

Eine abgeleitete Klasse muss stets auch im Kontext ihrer Basisklasse eingesetzt werden können. Sie darf das Verhalten der Basisklasse nur erweitern, aber nicht einschränken. Der Entwickler oder die Entwicklerin darf bei der Verwendung keine Überraschung – das heißt, ob mit der Basisklasse oder einer abgeleiteten Klasse gearbeitet wird – erleben.

Interface-Segregation-Prinzip

Der Umfang eines Interfaces wird durch die Anforderungen des Client bestimmt und nicht umgekehrt. Ein Client darf nicht gezwungen werden, Funktionalität zu implementieren, die gar nicht benötigt wird. Damit wird der Zusammenhalt von Modulen gestärkt, deren Kopplung jedoch reduziert.

Dependency-Inversion-Prinzip

In einer geschichteten Architektur existieren Klassen und Module auf unterschiedlichen Abstraktionsebenen. In Klassen niedriger Ebenen werden Implementierungsdetails - beispielweise die Datenbankanbindung - gekapselt. Auf den höheren Ebenen geht es dagegen um die abstrakte Business-Logik. Das Dependency-Inversion-Prinzip besagt, dass Module auf höheren Abstraktionsebenen nicht von Modulen niedrigerer Abstraktionsebenen abhängen, sondern sich jeweils auf Interfaces beziehen sollten. Interfaces hingegen sollten nicht von Details abhängen, sondern die Details müssen eine Abhängigkeit von den jeweiligen Interfaces aufweisen.

Die „saubere“ Architektur

Berücksichtigt ihr die SOLID-Prinzipien, dann führt das bereits zu einer guten Struktur der Softwarearchitektur. Wenn ihr diese Prinzipien dann noch mit Schichtenarchitektur und dem Prinzip der Abstraktion und Generalisierung kombiniert, dann ergibt sich daraus die sogenannte Clean Architecture. Wie ihr in der folgenden Abbildung erkennen könnt, sind die einzelnen Schichten ringförmig angeordnet.

Den Kern bildet die Business-Logik. Diese Logik besteht aus den Entitäten und Regeln, die aus der „realen Welt" gespiegelt werden. Die Business-Logik ändert sich nur selten und keinesfalls durch Änderungen an darüber liegenden Schichten. Die Software selbst soll sich nach dem Business richten und nicht umgekehrt. Für die Struktur in dieser Schicht kommt oft Domain Driven Design – das heißt, eine spezielle Herangehensweise an die Modellierung komplexer Software - zum Einsatz.

Der darüber liegende Ring der Anwendungslogik hängt vom Ablauf und Betrieb der Anwendung ab. Hier wird beispielsweise die Abfolge von Dialogen, also den Regeln, nach denen Validierungen durchzuführen sind, definiert.

Darüber liegt die Schicht der Schnittstellen und Adapter. Die Entitäten aus der Business-Logik sind nicht kompatibel mit den Frameworks für Datenbanken und User Interfaces. Hier ist der richtige Ort, um die notwendigen Anpassungen vorzunehmen und beispielsweise Models, Views, ResultSets, Value Objects oder Entitäten zu konvertieren. Typische Komponenten dieser Schicht sind die Adapter zur Datenbank oder die Model-View-Controller-Architektur (MVC) des eingesetzten UI Frameworks.

Zum äußersten Ring gehören Frameworks und Treiber – etwa zu Datenbanken oder anderen externen Schnittstellen.

Fazit

Wenn ihr euch fragt, warum ihr überhaupt einen „sauberen“ Code schreiben solltet, ist die Antwort recht einfach: Ein schlechter Code behindert die Arbeit, erschwert die Wartung von Software und erhöht die Kosten für die Weiterentwicklung. Um euch die Erstellung von gutem Code zu erleichtern, solltet ihr auf die SOLID-Prinzipien zurückgreifen. Diese könnt ihr auch als eine Art Checkliste betrachten, die euch bei der Softwareentwicklung unterstützt, indem bereits von vornherein Fehler und kritische Konstrukte vermieden werden.

Wenn ihr euch für das Thema „Clean Code“ interessiert, dann werft auch einen Blick in den zweiteiligen adesso-Blog-Beitrag von Markus Wagner zum Thema Clean Code Developer. Zudem kann ich euch das Buch „Clean Architecture“ von Robert C. Martin empfehlen.

Fazit

Wenn ihr euch fragt, warum ihr überhaupt einen „sauberen“ Code schreiben solltet, ist die Antwort recht einfach: Ein schlechter Code behindert die Arbeit, erschwert die Wartung von Software und erhöht die Kosten für die Weiterentwicklung. Um euch die Erstellung von gutem Code zu erleichtern, solltet ihr auf die SOLID-Prinzipien zurückgreifen. Diese könnt ihr auch als eine Art Checkliste betrachten, die euch bei der Softwareentwicklung unterstützt, indem bereits von vornherein Fehler und kritische Konstrukte vermieden werden.

Wenn ihr euch für das Thema „Clean Code“ interessiert, dann werft auch einen Blick in den zweiteiligen adesso-Blog-Beitrag von Markus Wagner zum Thema Clean Code Developer. Zudem kann ich euch das Buch „Clean Architecture“ von Robert C. Martin empfehlen.

Bild Wolfgang   Wünsche

Autor Wolfgang Wünsche

Wolfgang Wünsche ist Senior Software Engineer bei adesso. Seinen Arbeitsschwerpunkt bildet die Java-Enterprise-Entwicklung. Darüber hinaus beschäftigt er sich intensiv mit DevOps-Themen.

Diese Seite speichern. Diese Seite entfernen.