Sofortantwort
SOLID-Prinzipien einfach erklärt
Fünf Prinzipien, die die Wartbarkeit und Erweiterbarkeit von Code fördern.
- Kurz gesagt
- S – Single Responsibility: Jede Klasse hat genau eine Aufgabe
- Typischer Einsatz
- Plugin-Architekturen, Testbarkeit, KI-Provider-Abstraktion
- Wichtig zu wissen
- D – Dependency Inversion: Abhängigkeiten auf Abstraktionen, nicht auf Implementierungen
SOLID-Prinzipien im Überblick
Die SOLID-Prinzipien sind fünf grundlegende Designprinzipien für objektorientierte Software, die wartbaren, erweiterbaren und testbaren Code fördern. Sie wurden von Robert C. Martin (Uncle Bob) formuliert und sind heute Standard in professioneller Softwareentwicklung. Für KI-Systeme sind SOLID-Prinzipien besonders relevant: ML-Pipelines, Inferenz-Services und Daten-Prozessoren werden schnell komplex – gute Architektur verhindert, dass sie unwartbar werden.
SOLID ist ein Akronym für fünf Prinzipien, die Robert C. Martin (Uncle Bob) populär gemacht hat. Sie helfen dabei, Software zu bauen, die sich leicht ändern und erweitern lässt.
Warum ist das wichtig?
Ohne gute Struktur wird Code schnell zum “Spaghetti-Code”: Alles hängt mit allem zusammen, eine kleine Änderung bricht zehn andere Stellen. SOLID verhindert das.
Die fünf Prinzipien auf einen Blick:
| Buchstabe | Prinzip | Bedeutung |
|---|---|---|
| S | Single Responsibility | Jede Klasse macht nur eine Sache |
| O | Open/Closed | Erweiterbar ohne bestehenden Code zu ändern |
| L | Liskov Substitution | Unterklassen müssen wie ihre Oberklasse funktionieren |
| I | Interface Segregation | Kleine, spezifische Schnittstellen |
| D | Dependency Inversion | Abhängigkeiten über Abstraktionen, nicht konkrete Klassen |
Praxis-Beispiel:
Stell dir vor, du baust einen KI-Chatbot. Ohne SOLID hättest du eine riesige Klasse, die API-Calls macht, Datenbank speichert, E-Mails sendet und Logs schreibt. Mit SOLID hast du kleine, austauschbare Bausteine – du kannst den LLM-Provider wechseln, ohne den Rest anzufassen.
Technisch betrachtet
S – Single Responsibility Principle
Eine Klasse sollte nur einen Grund haben, sich zu ändern.
// ❌ Schlecht: Klasse macht zu viel
class UserService {
createUser() { ... }
sendWelcomeEmail() { ... }
generateReport() { ... }
}
// ✅ Gut: Getrennte Verantwortlichkeiten
class UserService { createUser() { ... } }
class EmailService { sendWelcomeEmail() { ... } }
class ReportService { generateReport() { ... } }
O – Open/Closed Principle
Offen für Erweiterung, geschlossen für Änderung.
// ✅ Neue Embedding-Provider hinzufügen ohne bestehenden Code zu ändern
interface EmbeddingProvider {
embed(text: string): Promise<number[]>;
}
class OpenAIProvider implements EmbeddingProvider { ... }
class CohereProvider implements EmbeddingProvider { ... }
// Neuer Provider → neue Klasse, kein bestehender Code geändert
D – Dependency Inversion Principle
Abhängigkeiten auf Abstraktionen, nicht auf konkrete Implementierungen.
// ❌ Direkte Abhängigkeit
class RAGService {
private db = new PineconeClient(); // fest verdrahtet
}
// ✅ Abstraktion
class RAGService {
constructor(private vectorStore: VectorStore) {} // austauschbar
}Schritt für Schritt
Wie SOLID-Prinzipien zu besseren Grenzen führen
Änderungsgründe sichtbar machen
Untersuche, welche fachlichen oder technischen Gründe einen Baustein verändern können. Wenn viele unabhängige Gründe in derselben Klasse oder Funktion liegen, entsteht eine schwer steuerbare Kopplung.
Schnittstellen am Bedarf ausrichten
Formuliere kleine Verträge für die tatsächlichen Nutzer einer Abhängigkeit. Eine Schnittstelle sollte nicht dazu zwingen, Funktionen zu kennen oder zu implementieren, die für die jeweilige Aufgabe nicht gebraucht werden.
Implementierungen austauschbar halten
Lasse Geschäftslogik von Abstraktionen abhängen, wenn mehrere Varianten, externe Dienste oder Tests dies sinnvoll machen. Die Abstraktion soll ein echtes Verhalten ausdrücken, nicht nur zusätzliche Schichten schaffen.
Auswirkungen mit konkreten Änderungen prüfen
Bewerte die Struktur anhand realer Änderungsfälle und Tests. Wenn eine Regel das Verständnis erschwert oder keine plausible Änderung erleichtert, ist sie kein Selbstzweck.
Konkretes Beispiel
Eine Benachrichtigung wird austauschbar
Ein System sendet Statusmeldungen direkt über einen einzelnen Anbieter. Für Tests und einen späteren Wechsel müssten viele Teile der Geschäftslogik geändert werden.
Direkte Kopplung
Fachliche Entscheidung, Formatierung und Kommunikation mit dem externen Dienst liegen in derselben Komponente. Fehler beim Anbieter sind schwer testbar und berühren unmittelbar den Kernablauf.
Klarer Vertrag
Die Geschäftslogik spricht einen kleinen Benachrichtigungs-Vertrag an. Konkrete Anbieter und Test-Doubles erfüllen diesen Vertrag, während Fehler und Ausfälle an einer kontrollierten Grenze behandelt werden.
Einordnung
Vorteile und Grenzen
Das spricht dafür
- Macht Verantwortlichkeiten und Abhängigkeiten gezielt veränderbar und besser testbar.
- Reduziert die Reichweite einzelner Änderungen in komplexen Softwareteilen.
- Unterstützt klare Verträge zwischen Fachlogik, Infrastruktur und externen Diensten.
Das solltest du beachten
- Zu frühe Abstraktion kann einfache Software unnötig kompliziert und indirekt machen.
- Prinzipien lösen keine unklare Fachlichkeit oder fehlende Tests automatisch.
- Eine gute Schnittstelle braucht Pflege, wenn sich reale Anforderungen verändern.
Vertiefung · für FortgeschritteneVerantwortung und Abhängigkeiten gestalten
Veränderbare Grenzen
- Clean Code
- Praktiken für verständlichen und wartbaren Quellcode.
- Dependency Injection
- Abhängigkeiten werden von außen übergeben statt intern erstellt – für bessere Testbarkeit.
- Design Patterns
- Bewährte Lösungen für häufige Probleme in der Softwareentwicklung.
- Microservices
- Anwendung besteht aus vielen kleinen, unabhängigen Services.
- API
- Schnittstelle zur Kommunikation zwischen Softwaresystemen.