SOLID — fünf Prinzipien der objektorientierten Programmierung, formuliert von Robert C. Martin (Uncle Bob) in den frühen 2000er Jahren. Laut DigitalOcean, 2024, steht SOLID für Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation und Dependency Inversion. Diese Prinzipien bilden die Grundlage von Clean Architecture und werden in der Android-Entwicklung (MVP, MVVM, Clean Architecture) und iOS (VIPER, TCA) angewendet.
Wichtigste Erkenntnisse
SOLID — ein mnemonisches Akronym, das fünf Prinzipien des objektorientierten Designs darstellt. Der Begriff wurde von Robert C. Martin im Artikel „Design Principles and Design Patterns“ (2000) eingeführt und später im Buch „Agile Software Development: Principles, Patterns, and Practices“ (2002) popularisiert. SOLID ist kein Framework oder eine Bibliothek — es ist eine Sammlung von Praktiken, die Code weniger gekoppelt, testbarer und leichter änderbar machen.
Laut Clean Coder Blog, 2014, löst jedes SOLID-Prinzip ein bestimmtes Design-Problem: SRP bekämpft God-Klassen, OCP verhindert kaskadierende Änderungen, LSP schützt vor falscher Vererbung, ISP vermeidet große Schnittstellen und DIP reduziert starke Kopplung. Zusammen bilden sie die Grundlage von Clean Architecture, die in Android-Projekten mit MVP, MVVM und MVI verwendet wird.
Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung. Die Formulierung: „Eine Klasse sollte nur einen Grund zur Änderung haben.“ Das bedeutet, dass jedes Modul oder jede Klasse für genau eine Funktionalität oder eine Domänenentität verantwortlich ist. Wenn eine Klasse sowohl Benutzer als auch E-Mail-Versand verwaltet — hat sie zwei Änderungsgründe, was SRP verletzt.
Laut Robert C. Martin, 2002, ist SRP das wichtigste und gleichzeitig am häufigsten verletzte Prinzip. In der mobilen Entwicklung wird SRP oft in Activity/Fragment verletzt, indem UI-Logik, Navigation, Netzwerk und Geschäftslogik kombiniert werden. Die Lösung besteht darin, jede Schicht in eine separate Klasse zu extrahieren: ViewModel für UI-Logik, Repository für Daten, NavController für Navigation.
Betrachten Sie die Klasse UserManager, die ein Profil lädt, Einstellungen speichert und E-Mails sendet. Dies sind drei unterschiedliche Verantwortlichkeiten, die jeweils in eine separate Klasse extrahiert werden sollten: UserProfileRepository (Laden), UserSettingsStorage (Speichern) und EmailService (Senden). Der Client-Code (ViewModel) verwendet alle drei durch Dependency Injection, und jede Klasse wird isoliert einfach getestet und kann geändert werden, ohne andere zu beeinflussen.
// ❌ SRP-Verletzung: Activity kennt Netzwerk, DB und UI
class ProfileActivity : AppCompatActivity() {
fun loadProfile() {
api.getUser() // Netzwerkaufruf
db.saveUser() // DB-Operation
updateUI() // UI-Aktualisierung
}
}
// ✅ SRP eingehalten: Schichten sind getrennt
class ProfileViewModel : ViewModel() {
private val repo = UserRepository()
fun loadProfile() { repo.getUser() }
}
Anzeichen für SRP-Verletzung: eine Klasse mit mehr als 200 Zeilen, Methoden aus verschiedenen Domänen, häufige Änderungen aus unterschiedlichen Gründen. Für die Android-Entwicklung gilt die einfache Regel: Activity behandelt nur den Bildschirmlebenszyklus, ViewModel behandelt den UI-Zustand, Repository behandelt die Datenquellen.
Das SRP-Prinzip gilt nicht nur für Klassen, sondern auch für die Architektur auf Service-Ebene. Jeder Microservice behandelt eine Domänenentität: UserService — nur Benutzer, PaymentService — nur Zahlungen, NotificationService — nur Benachrichtigungen. Dies ermöglicht unabhängiges Skalieren, Bereitstellen und Testen von Diensten. In mobilen Anwendungen zeigt sich SRP auf Microservice-Ebene in der Trennung von API-Clients nach Domäne.
Open-Closed Principle (OCP) — Klassen sollten offen für Erweiterung sein (neues Verhalten kann hinzugefügt werden) und geschlossen für Modifikation (vorhandener Code wird nicht geändert). Dies wird durch Polymorphismus, abstrakte Klassen und Schnittstellen erreicht. Anstatt if-else zu einer vorhandenen Methode hinzuzufügen, wird eine neue Schnittstellenimplementierung erstellt.
Laut Clean Coder Blog, 2014, funktioniert OCP am besten mit dem Strategy-Muster. Wenn eine App beispielsweise verschiedene Zahlungsmethoden (Google Pay, Apple Pay, PayPal) unterstützt, muss dem Zahlungsprozessor kein switch-case hinzugefügt werden. Jede Zahlungsmethode implementiert eine gemeinsame PaymentGateway-Schnittstelle, und ein neues Zahlungssystem wird als neue Klasse hinzugefügt, ohne vorhandene zu ändern.
// ✅ OCP: offen für Erweiterung, geschlossen für Modifikation
interface PaymentGateway {
fun processPayment(amount: Double): Boolean
}
class GooglePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
// Neues Zahlungssystem — ohne Änderung des vorhandenen Codes
class ApplePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
Liskov Substitution Principle (LSP) — das Substitutionsprinzip von Barbara Liskov. Wenn S ein Subtyp von T ist, dann können Objekte des Typs T durch Objekte des Typs S ersetzt werden, ohne die Programmeigenschaften zu ändern. Formal: Eine Funktion, die eine Basisklasse verwendet, sollte mit jeder ihrer Unterklassen korrekt funktionieren. Wenn eine Unterklasse eine Ausnahme wirft, wo die Basisklasse keine wirft — ist LSP verletzt.
Laut Robert C. Martin, 2002, ist LSP das am schwersten zu verstehende SOLID-Prinzip. Das klassische Verletzungsbeispiel ist die Klasse Square, die von Rectangle erbt. Wenn setWidth bei Square sowohl Breite als auch Höhe setzt, erhält Client-Code, der Rectangle-Verhalten erwartet, ein unerwartetes Ergebnis. In der mobilen Entwicklung wird LSP oft beim Vererben von ViewModel verletzt — wenn eine Kind-ViewModel erforderliche Abhängigkeiten hinzufügt.
// ❌ LSP-Verletzung: Square bricht Rectangle-Verhalten
open class Rectangle(open var width: Int, open var height: Int)
class Square(side: Int) : Rectangle(side, side) {
override var width
get() = super.width
set(value) { super.setBoth(value, value) }
}
Interface Segregation Principle (ISP) — Clients sollten nicht von Schnittstellen abhängen, die sie nicht nutzen. Statt einer einzigen „fetten“ Schnittstelle erstellen Sie mehrere schmale, spezialisierte Schnittstellen. Wenn eine Klasse eine Schnittstelle implementiert, aber einige Methoden UnsupportedOperationException werfen oder leer bleiben — ist dies ein klares Zeichen für eine ISP-Verletzung.
Laut DigitalOcean, 2024, ist ISP besonders relevant in der mobilen Entwicklung beim Entwurf von ViewModel und Repository. Statt einer einzigen UserRepository-Schnittstelle mit allen CRUD-Methoden ist es besser, QueryUserRepository (nur Lesen) und CommandUserRepository (Schreiben) zu erstellen. Dann hängt ein Nur-Lese-Client (UI-Element) nur von der Query-Schnittstelle ab und weiß nichts von Schreibmethoden.
// ❌ Große Schnittstelle — Client muss unnötige Methoden implementieren
interface UserOperations {
fun getUser(id: String): User
fun saveUser(user: User)
fun deleteUser(id: String)
fun exportUsers(): File
}
// ✅ ISP: getrennte Schnittstellen
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }
Dependency Inversion Principle (DIP) — High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beide sollten von Abstraktionen (Schnittstellen) abhängen. Abstraktionen sollten nicht von Details abhängen — Details sollten von Abstraktionen abhängen. Dies ist nicht „Dependency Injection“ (DI), obwohl DI eine häufige Methode zur Implementierung von DIP ist.
Laut Robert C. Martin, 2019, ist DIP die Grundlage von Clean Architecture. ViewModel (High-Level) sollte nicht direkt eine RetrofitApi-Instanz (Detail) erstellen. Stattdessen hängt ViewModel von einer UserRepository-Schnittstelle ab, und die konkrete UserRepositoryImpl mit Retrofit wird durch den Konstruktor übergeben. In Android wird DIP durch Hilt/Dagger oder Koin implementiert: Alle Abhängigkeiten werden durch den DI-Container bereitgestellt.
// ✅ DIP: Module hängt von Abstraktion ab, nicht von Details
class UserRepositoryImpl(
private val api: UserApi, // Hängt von Schnittstelle ab
private val db: UserDao // Hängt von Schnittstelle ab
) : UserRepository {
override suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// Hilt DI: Details werden durch das DI-Modul verbunden
@Module
object NetworkModule {
@Provides
fun provideUserApi(retrofit: Retrofit): UserApi =
retrofit.create(UserApi::class.java)
}
SOLID in der mobilen Entwicklung wird auf allen Ebenen angewendet: von der Anwendungsarchitektur bis zu einzelnen Klassen. In Android-Projekten teilt Clean Architecture den Code in drei Schichten: Domain (Geschäftslogik — unabhängig von Frameworks), Data (Repositories, API, DB) und Presentation (UI, ViewModel). Die Domain-Schicht verwendet SOLID-Prinzipien: Anwendungsfälle (SRP), Repository-Schnittstellen (DIP), Entitätsklassen (OCP + LSP).
Laut Android Developers Guide, 2025, zeigt sich SRP in Android in der Trennung von ViewModel, Repository und Mapper. OCP — beim Hinzufügen neuer Datenquellen durch die DataSource-Schnittstelle. LSP — in der einheitlichen Ergebnisverarbeitung über verschiedene Repositories hinweg. ISP — im CQRS-Ansatz (Trennung von Read/Write-Repositories). DIP — durch Hilt/Koin für Dependency Injection.
| Prinzip | Problem ohne es | Lösung im mobilen Projekt |
|---|---|---|
| SRP | Activity mit 1000+ Zeilen | ViewModel + UseCase + Repository |
| OCP | switch-case nach Zahlungsart | Strategy: PaymentGateway-Schnittstelle |
| LSP | Fehler beim Ersetzen von BaseViewModel | Unterklassenvertrag prüfen |
| ISP | UnsupportedOperationException | Reader / Writer-Trennung |
| DIP | ViewModel erstellt Retrofit manuell | Hilt / Koin DI-Container |
SOLID-Fehler hängen meist mit übermäßiger Code-Verkomplizierung zusammen. Der erste — Befolgen der Prinzipien wörtlich ohne Berücksichtigung des Kontexts. Aufteilung einer UserService-Klasse in 10 Schnittstellen und 15 Klassen nur für ein „sauberes“ ISP ist Overengineering. SOLID ist ein Werkzeug, kein Ziel. Der zweite Fehler — Verwechslung von SRP mit „eine Methode = eine Verantwortung“. Eine Klasse kann mehrere Methoden haben, wenn sie alle zum selben Verantwortungsbereich gehören.
Laut Simple Thread, 2024, der dritte Fehler — Ignorieren von LSP beim Vererben von ViewModel in Android. Wenn die Basis-ViewModel LiveData erwartet, die Kind-ViewModel aber StateFlow verwendet — erhält der auf LiveData abonnierte Client-Code keine Updates. Der vierte — Verletzung von DIP für Tests: RepositoryImpl erstellt direkt eine OkHttpClient-Instanz, was Unit-Tests unmöglich macht.
Die goldene Regel: Wenden Sie SOLID an, wenn es ein echtes Problem löst (häufige Änderungen, Tests Schwierigkeit, Duplikation). Für einfache CRUD-Bildschirme ist die strikte Befolgung aller fünf Prinzipien übertrieben. Für Geschäftslogik, Finanzberechnungen und API-Interaktionen ist SOLID unerlässlich.
Clean Architecture (Robert C. Martin, 2012) — direkte Anwendung von SOLID auf Anwendungsschichtebene. SRP definiert die Grenzen der Anwendungsfälle (jeder Anwendungsfall — eine Klasse). OCP wird durch Repository-Schnittstellen implementiert (Data Layer kann sich ändern, ohne Domain zu modifizieren). ISP bietet die Trennung des Anwendungsfalls in Input/Output-Grenzen. DIP — Abhängigkeitsrichtung nach innen zur Domain-Schicht. LSP garantiert, dass jede Repository-Implementierung austauschbar ist, ohne Anwendungsfälle zu brechen.
Häufig gestellte Fragen
SOLID — fünf Regeln zum Schreiben von Code, der leicht zu ändern, zu testen und zu verstehen ist. Jeder Buchstabe ist ein Prinzip: schreiben Sie keine großen Klassen (SRP), ändern Sie keinen vorhandenen Code — fügen Sie neuen hinzu (OCP), brechen Sie nicht das Verhalten von Unterklassen (LSP) und andere.
SRP (Single Responsibility) gilt als das wichtigste, weil seine Verletzung zu God-Klassen führt — riesigen Klassen, die schwer zu testen und zu ändern sind. Ohne DIP (Dependency Inversion) bleibt der Code jedoch stark gekoppelt, was ebenfalls kritisch ist.
Nicht obligatorisch, aber für kommerzielle Projekte mit langem Lebenszyklus sehr empfehlenswert. Für einfache Apps (ein Bildschirm, keine Geschäftslogik) kann SOLID übertrieben sein. Für Projekte mit 50+ Bildschirmen und 3+ Entwicklern ist SOLID das notwendige Minimum.
Konsequenzen: Klassen werden „fett“ (1000+ Zeilen), eine Änderung an einer Stelle bricht drei andere, Unit-Tests werden unmöglich, das Hinzufügen einer neuen Funktion dauert Wochen statt Tage. Mit der Zeit wird der Code zu einem „Big Ball of Mud“ — verworren und zerbrechlich.
Anzeichen für Einhaltung: jede Klasse unter 200 Zeilen, Ändern einer Funktion betrifft nicht 5+ Dateien, Tests können ohne Mocken von 10 Abhängigkeiten geschrieben werden, ein neuer Entwickler versteht die Struktur an einem Tag. Tools wie SonarQube und detekt helfen, SRP- und DIP-Verletzungen zu erkennen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch