Dependency Injection in Kotlin: Ein einfacher Leitfaden für Anfänger

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Was ist Dependency Injection und warum wird es in Kotlin benötigt?



Dependency Injection (DI, Abhängigkeitsinjektion) ist eines der Schlüsselprinzipien des Designs in der modernen Softwareentwicklung, insbesondere in Kotlin. Wenn Sie gerade erst mit dem Programmieren beginnen, mag dieses Konzept komplex erscheinen. In Wirklichkeit ist es ganz einfach: Dependency Injection ist eine Methode, einem Objekt seine Abhängigkeiten von außen zu übergeben, anstatt sie innerhalb des Objekts selbst zu erstellen.



Lassen Sie es uns an einem alltäglichen Beispiel verdeutlichen. Stellen Sie sich vor, Sie backen einen Kuchen. Anstatt jedes Mal selbst Mehl zu mahlen, Eier zu sammeln und Butter zu schlagen (Abhängigkeiten innerhalb erstellen), gehen Sie einfach in den Laden und kaufen fertige Zutaten (Abhängigkeiten von außen beziehen). DI funktioniert nach dem gleichen Prinzip: Sie „bestellen“ fertige Komponenten, anstatt sie manuell zu erstellen.



In Kotlin ist DI besonders wichtig, da die Sprache häufig für die Entwicklung von Android-Apps, serverseitigen Lösungen mit Ktor und Microservices verwendet wird. Ohne DI wird der Code stark gekoppelt, schwer testbar und wartbar. Mit DI erhalten Sie:

  • Flexibilität — einfaches Austauschen einer Implementierung gegen eine andere
  • Testbarkeit — Sie können Mock-Objekte einfügen
  • Sauberen Code — Klassen wissen nicht, wie ihre Abhängigkeiten erstellt werden


Das Problem ohne Dependency Injection



Betrachten wir eine typische Situation. Wir haben eine Klasse UserService, die mit einer Datenbank arbeitet. Ohne DI sieht das so aus:



class UserService {    private val database = Database() // Starke Kopplung

fun getUser(id: Int): User? { return database.query("SELECT * FROM users WHERE id = $id") }}


Welche Probleme gibt es hier? Erstens erstellt UserService selbst eine Instanz von Database. Wenn wir die echte Datenbank durch eine Testdatenbank ersetzen möchten (z. B. In-Memory), müssten wir den Code innerhalb der Klasse ändern. Zweitens wird das Unit-Testen unmöglich – wir können keine gefälschte Datenbank einfügen. Drittens verstößt die Klasse gegen das Single-Responsibility-Prinzip (SRP): Sie verarbeitet nicht nur Benutzer, sondern verwaltet auch die Erstellung der Datenbank.



Stellen Sie sich vor, es gibt Dutzende solcher Abhängigkeiten – der Code verwandelt sich in „Spaghetti“, wo alles mit allem verbunden ist. Genau zur Lösung dieses Problems gibt es Dependency Injection.



Manuelle Implementierung von Dependency Injection



Der einfachste Weg, Abhängigkeiten zu injizieren, ist die Übergabe über den Konstruktor. Dies nennt man Konstruktorinjektion. Schreiben wir unser Beispiel um:



class UserService(private val database: Database) {    fun getUser(id: Int): User? {        return database.query("SELECT * FROM users WHERE id = $id")    }}

// Verwendung:val database = Database()val userService = UserService(database)


Jetzt weiß UserService nicht, wie Database erstellt wird. Er erhält einfach eine fertige Instanz. Wir können die Datenbank leicht austauschen:



class FakeDatabase : Database() {    override fun query(sql: String): User? {        return User(1, "Testbenutzer")    }}

// In Tests:val fakeDb = FakeDatabase()val userService = UserService(fakeDb)


Dieser Ansatz wird manuelles DI genannt. Für kleinere Projekte ist er völlig ausreichend. Sie erstellen einfach alle Abhängigkeiten an einer Stelle (z. B. in main() oder in einer speziellen Container-Klasse) und übergeben sie dorthin, wo sie benötigt werden.



Allerdings hat manuelles DI einen Nachteil: Wenn das Projekt wächst, wird die Anzahl der Abhängigkeiten enorm. Sie müssen die Reihenfolge der Objekterstellung manuell verfolgen und sie über mehrere Ebenen hinweg übergeben. Hier kommen DI-Frameworks ins Spiel.



Dependency Injection mit Koin



Koin ist ein beliebtes DI-Framework für Kotlin, das keine Codegenerierung erfordert

Blogs

Buchempfehlungen