Wie du Software entwickelst, die sich leicht skalieren lässt

Digitale Lösungen werden im Laufe der Zeit häufig verändert. Damit diese Anpassungen reibungslos verlaufen, sollten Entwickler die fünf SOLID-Prinzipien für gutes Softwaredesign befolgen. 

Ein erfolgreiches Softwareprodukt entwickelt sich ständig weiter. Es wird verbessert, erhält neue Funktionen, zusätzliche Schnittstellen und vieles mehr. Wenn die Software jedoch nicht von Anfang an gut aufgesetzt worden ist, wird es immer schwieriger, diese Änderungen umzusetzen.

Wer das vermeiden möchte, sollte die fünf SOLID-Prinzipien für gutes Softwaredesign befolgen, die der amerikanische Softwareentwickler, IT-Berater und Schriftsteller Robert C. Martin im Jahr 2000 eingeführt hat.

Auf der Grundlage dieser Prinzipien entsteht Software, die folgende Vorteile aufweist:

    • Einfach instand zu halten und zu testen
    • Problemlos zu erweitern und wiederzuverwenden
    • Leicht zu verstehen

»Gute Softwaresysteme beginnen mit sauberem Code. Denn, wenn die Ziegelsteine nicht gut gemacht sind, spielt einerseits die Architektur des Gebäudes keine große Rolle. Andererseits kann man mit gut gemachten Ziegeln ein beträchtliches Durcheinander anrichten. Hier kommen die SOLID-Prinzipien ins Spiel. «

Robert C. Martin

Das Akronym SOLID steht für: 

    • S – Single-Responsibility-Prinzip  
    • O – Open-Closed-Prinzip  
    • L – Liskovsches Substitutionsprinzip  
    • I – Interface-Segregation-Prinzip
    • D – Dependency-Inversion-Prinzip

Wir werden jedes Prinzip einzeln vorstellen, um zu zeigen, wie SOLID dazu beitragen kann, bessere Software zu entwickeln, die sich leicht skalieren lässt.

💡 Glossar

Funktionen sind in sich geschlossene Module aus Code, die eine bestimmte Aufgabe erfüllen sollen. Normalerweise umfasst dies den Empfang und die Verarbeitung von Daten und die Rückgabe eines Ergebnisses.

Datenstrukturen sind Formate zum Abrufen, Organisieren, Verarbeiten und Speichern von Daten. Sie ordnen Informationen für einen bestimmten Zweck und erleichtern den Benutzern den Zugriff auf die Informationen und die Arbeit damit.

Die SOLID-Prinzipien legen fest, wie Funktionen und Datenstrukturen in Klassen geordnet und wie diese Klassen miteinander verbunden werden sollten.  Die Verwendung des Wortes „Klasse“ bedeutet nicht, dass diese Prinzipien nur in der objektorientierten Programmierung angewendet werden.  In diesem Fall beschreibt das Wort „Klasse“ eine bestimmte Kombination oder Gruppe von Funktionen und Daten. Die fünf Prinzipien sollten auf diese Gruppen angewandt werden.

1. Das Single-Responsibility-Prinzip (SRP) 

Robert C. Martin definiert das erste Prinzip wie folgt:

»Es sollte nie mehr als einen Grund geben, eine Klasse zu modifizieren.«

Robert C. Martin 

Dieses Prinzip wird jedoch in der Regel auf mehr als eine Weise beschrieben. Weitere Formulierungen sind:

„Ein Modul sollte für einen, und nur einen, Akteur verantwortlich sein. “

„Fasse Dinge zusammen, die sich aus denselben Gründen ändern. Trenne Dinge, die sich aus unterschiedlichen Gründen ändern.“

Two colleagues of the Offshore team of L-One Systems
Die Entwickler von L-One Systems verbessern ständig ihre Skills im Softwaredesign, z. B. die Anwendung der SOLID-Prinzipien. Sie verwenden 10 Prozent ihrer Arbeitszeit für die Weiterbildung

Robert C. Martin erklärt: „Von allen SOLID-Prinzipien ist das Single-Responsibility-Principle (SRP) wohl das, das am wenigstens verstanden wird. Das liegt wahrscheinlich daran, dass das SRP einen besonders unpassenden Namen hat. Es passiert zu schnell, dass Entwickler den Namen hören und dann annehmen, dass es bedeutet, dass jede Klasse nur eine Sache tun sollte.“

Leider ist das ein Irrtum. Das gilt allerdings für Funktionen: Eine Funktion sollte eine, und nur eine Sache tun. 

„Responsibilities“ sind die primäre Achse für Veränderungen

Martin definiert eine „responsibility“ als einen Grund für Veränderungen und kommt zu dem Schluss, dass eine Klasse/ein Modul nur einen Grund haben sollte, geändert zu werden. Wenn sich mehr als ein Grund für die Änderung einer Klasse finden lassen, hat diese Klasse mehr als eine responsibility. Dies ist manchmal schwer zu erkennen, da wir daran gewöhnt sind, responsibilities in Gruppen zu denken.  

Was ist das Problem, wenn eine Klasse/ein Modul mehr als eine responsibility hat?  Warum ist es wichtig, diese responsibilities in mehrere Klassen/Module aufzuteilen?  

Der Grund ist, dass jede responsibility als primäre Achse für Veränderungen fungiert. Wenn sich also die Softwareanforderungen ändern (und das werden sie), spiegeln sich diese Änderungen in den Responsibilities wider. Wenn diese Responsibilities für mehrere Klassen/Module gelten, haben Letztere mehr als einen Grund, sich zu ändern. Das Ergebnis: Die Umsetzung der Änderungen wird viel Aufwand erfordern.

Warum ist das Single-Responsibility-Prinzip wichtig? Ein Beispiel

Der folgende Fall verdeutlicht die Probleme, die auftreten, wenn Entwickler das Single-Responsibility-Prinzip nicht befolgen.

Stelle dir vor, dass wir eine „Person“-Klasse haben, die wie in der Abbildung unten dargestellt, aufgebaut ist:

L-One Systems: Infografik einer “Person-Klasse”
Dieses Design verstößt gegen das erste der SOLID-Prinzipien, das Single-Responsibility-Prinzip (SRP)

Die Abbildung zeigt, dass die Person-Klasse zwei responsibilities hat. Die Erste besteht darin, der Personalabteilung die Stunden zu melden, die Zweite stellt der Buchhaltung Lohnabrechnungsfunktionen zur Verfügung. Beide Funktionen verwenden die Methode „regularHours“, die die regulären Arbeitszeiten der Angestellten, einschließlich der Pausenzeiten, zurückgibt.

Indem die Entwickler den Quellcode für diese beiden Zuständigkeiten in einer einzigen Klasse „Person“ zusammengefasst haben, haben sie die beiden Akteure, die Personalabteilung und die Buchhaltung, gekoppelt.

Wie kann sich das negativ auswirken?

Stelle dir vor, die Personalabteilung würde den Entwickler bitten, die regulären Stunden so zu ändern, dass sie keine Pausenzeiten mehr enthalten. Dann würde der Entwickler die Methode „regularHours“ so ändern, dass die Pausenzeit ausgeschlossen wird.

Dies würde jedoch die Funktionalität von reportPayroll beeinträchtigen. Denn die Buchhaltung geht immer noch davon aus, dass die Methode regularHours die Zeit einschließlich der Pausenzeit zurückgibt. Die Berechnungen der Abteilung basieren auf dieser Annahme.

Wenn der Entwickler vergisst zu prüfen, welche anderen Methoden die regularHours-Methode verwenden, würde dies zu falschen Abrechnungsdaten führen, ohne dass es jemand merkt.

Die Kopplung verhindert die Wiederverwendung der Funktionalitäten innerhalb der Klasse „Person“. Außerdem kann es dazu führen, dass die Aktionen der Personalabteilung etwas beeinflussen, wovon die Buchhaltung abhängig ist und umgekehrt. Mit anderen Worten: Es gibt mehr als einen Grund, eine Person zu ändern.

Digitale Lösungen leicht skalierbar machen

Das Single-Responsibility Prinzip ist eines der einfachsten Prinzipien, aber auch eines, das am schwierigsten umzusetzen ist. Wir verbinden responsibilities häufig automatisch. Das Auffinden und Trennen dieser responsibilities ist ein wesentlicher Bestandteil des Softwaredesigns. 

Wenn eine Funktion, ein Modul oder eine Klasse nur einen einzigen Grund hat, sich zu ändern, bedeutet dies, dass sie dem Single-Responsibility Prinzip entsprechen.  Dadurch lassen sich Änderungen leicht umsetzen und die digitale Lösung lässt sich problemlos skalieren.

The developers of L-One Systems are applying the SOLID principles
Das L-One Offshore-Team setzt in seiner täglichen Arbeit hohe Standards für die Softwareentwicklung ein

2. Das Open-Closed Principle (OCP) 

Robert C. Martin definiert das zweite Prinzip wie folgt:

»Software-Entitäten (Klassen, Module, Funktionen usw.) sollten offen für Erweiterungen, aber geschlossen für Veränderungen sein.«

Robert C. Martin 

Wenn eine einzige Änderung in einer Anwendung zu einer Kaskade von Änderungen an abhängigen Modulen führt – mit anderen Worten, massive Änderungen an der Software erzwingt – dann ist das Design nicht ideal. 

Wenn das Open-Closed-Prinzip gut angewandt wird, werden Änderungen durch das Hinzufügen von neuem Code erreicht, nicht durch die Änderung von altem, bereits funktionierendem Code. 

Mehr Flexibilität, Wiederverwendbarkeit und Wartbarkeit

Das Ziel des Open-Closed-Prinzips ist es, ein System erweiterbar zu machen, ohne dass es zu gravierenden Änderungen kommt.

Die Anwendung dieses Prinzips führt zu den größten Vorteilen, die der objektorientierten Technologie zugeschrieben werden: Flexibilität, Wiederverwendbarkeit und Wartbarkeit.

Klassen/Module, die dem Open-Closed-Prinzip entsprechen, haben zwei primäre Eigenschaften:

1) Sie sind offen für Erweiterungen:

Das Verhalten des Moduls kann erweitert werden. Das heißt, wenn sich die Anforderungen ändern, lässt sich das Modul um neue Verhaltensweisen erweitern, die diese Änderungen erfüllen. Das heißt, die Entwickler sind in der Lage, das Verhalten des Moduls zu ändern.

2) Sie sind geschlossen für Änderungen:

Durch das Hinzufügen oder Erweitern des Verhaltens dieser Module wird ihr Quellcode nicht verändert, das heißt, die ausführbare Form der Module wird nicht geändert.

Abstraktion nutzen, aber richtig

Es ist klar, dass diese beiden Attribute miteinander in Konflikt stehen, denn der normale Weg, das Verhalten eines Moduls zu erweitern, besteht darin, seinen Quellcode anzupassen.

Die Frage ist also: Wie können wir das Verhalten eines Moduls ändern, ohne seinen Quellcode anzupassen?  

Die Antwort lautet: durch die Nutzung von Abstraktion. Durch den Prozess der Abstraktion verbirgt ein Programmierer alle irrelevanten Daten eines Objekts, um die Komplexität zu reduzieren und die Effizienz zu erhöhen.

Es gibt einige Entwurfsmuster, mit denen sich das Open-Closed-Prinzip gut anwenden lässt, zum Beispiel die Folgenden:

 

Die Entwickler sind jedoch dafür verantwortlich, die Abstraktion nur auf diejenigen Teile der Lösung anzuwenden, die sich häufig ändern. Verfrühter Abstraktion zu widerstehen, ist ebenso wichtig wie die Abstraktion selbst.

Du möchtest mehr erfahren?

Bleib‘ dran! In Kürze werden wir den zweiten Teil dieses Artikels veröffentlichen, in dem wir die übrigen drei Prinzipien erläutern:

    • Liskovsches Substitutionsprinzip  
    • Interface-Segregation-Prinzip
    • Dependency-Inversion-Prinzip

Garantiert nicht verpassen, so geht's:

Mehr erkunden

› Hat mein digitales Produkt ein stabiles technisches Fundament?
Das findest du hier heraus!

Schnell Fehler im Code entdecken und Zusammenarbeit optimieren mit GitHub:
Lerne das Tool jetzt kennen!