Dependency Injection in TypeScript: Ein Leitfaden mit InversifyJS

Dependency Injection in TypeScript: Ein Leitfaden mit InversifyJS
Aktualisiert am 8.10.2024
Tobias Bück

Tobias Bück

Hallo! Ich bin Tobias Bück, Gründer von Softoft. Als Informatiker teile ich auf meinem Blog Einblicke in Softwareentwicklung, insbesondere das OTOBO Ticketsystem, sowie Automatisierungstools wie UIPath und Zapier.

In diesem Artikel erklären wir, wie du Dependency Injection in TypeScript mit der InversifyJS-Bibliothek umsetzt. Ziel ist es, die Vorteile von Dependency Injection zu erläutern und zu zeigen, wie du deinen Code durch den Einsatz von InversifyJS effizienter und wartungsfreundlicher gestalten kannst.

Was ist Dependency Injection in TypeScript?

Dependency Injection (DI) ist ein Entwurfsmuster, das die Kopplung zwischen Klassen reduziert (Loose Coupling). Es ermöglicht, dass Klassen über Schnittstellen oder Abstraktionen kommunizieren, anstatt direkt miteinander zu arbeiten. DI erhöht die Testbarkeit und Flexibilität des Codes. In TypeScript gibt es mit InversifyJS eine leistungsstarke Bibliothek, die diese Implementierung vereinfacht.

Warum Dependency Injection?

Ohne Dependency Injection sind Klassen oft fest an konkrete Implementierungen gebunden. Dies erschwert spätere Änderungen und erhöht die Fehleranfälligkeit. DI löst dieses Problem, indem es die Abhängigkeiten zentral verwaltet und automatisch bereitstellt.

Dependency Injection in TypeScript mit einem Wettervorhersage-System

Als Beispiel entwickeln wir ein kleines Wettervorhersage-System mit zwei verschiedenen Vorhersagern: "WeatherSatellite" und " WeatherStation". Beide Vorhersager implementieren die Wettervorhersage, jedoch wollen wir das System so gestalten, dass die konkrete Vorhersagemethode flexibel bleibt.

Beispiel ohne Dependency Injection

class WeatherSatellite {
    getTemperature(): number {
        return 20;
    }
}

class WeatherStation {
    getTemperature(): number {
        return 22;
    }
}

class Person {
    private weatherSatellite: WeatherSatellite;

    constructor() {
        this.weatherSatellite = new WeatherSatellite();
    }
}

UML-Diagramm: Aktuelles Design ohne Dependency Injection

UML Diagramm: Beispiel ohne Dependency Injection
UML Diagramm: Beispiel ohne Dependency Injection

Problem: Die Person-Klasse entscheidet, welche Wettervorhersagemethode verwendet wird. Das führt zu einer starken Kopplung und erschwert es, die Implementierung zu ändern oder zu erweitern.

Verbesserung: Einführung von Interfaces

In TypeScript können wir Interfaces nutzen, um Abhängigkeiten zu abstrahieren. Hier definieren wir ein IWeatherForecasting-Interface:

interface IWeatherForecasting {
    getTemperature(): number;
}

class WeatherSatellite implements IWeatherForecasting {
    getTemperature(): number {
        return 20;
    }
}

class WeatherStation implements IWeatherForecasting {
    getTemperature(): number {
        return 22;
    }
}

class Person {
    private weatherForecasting: IWeatherForecasting;

    constructor(weatherForecasting: IWeatherForecasting) {
        this.weatherForecasting = weatherForecasting;
    }
}

UML-Diagramm: Interface Implementierung ohne Dependency Injection

UML Diagramm: Interface
UML Diagramm: Interface

Problem: Obwohl jetzt ein Interface verwendet wird, entscheidet die Person-Klasse immer noch, welche Implementierung genutzt wird. Das widerspricht dem DI-Prinzip.

Lösung: Dependency Injection mit InversifyJS

Um diese Abhängigkeit vollständig zu lösen, verwenden wir InversifyJS, um die benötigten Objekte automatisch zu injizieren.

Code-Beispiel: Verwendung von InversifyJS für Dependency Injection

Zuerst müssen wir InversifyJS und reflect-metadata installieren:

npm install inversify reflect-metadata

Erstelle dann den TypeScript-Code mit InversifyJS:

import 'reflect-metadata';
import {Container, injectable, inject} from 'inversify';

const TYPES = {
    IWeatherForecasting: Symbol.for('IWeatherForecasting')
};

interface IWeatherForecasting {
    getTemperature(): number;
}

@injectable()
class WeatherSatellite implements IWeatherForecasting {
    getTemperature(): number {
        return 20;
    }
}

@injectable()
class WeatherStation implements IWeatherForecasting {
    getTemperature(): number {
        return 22;
    }
}

@injectable()
class Person {
    private weatherForecasting: IWeatherForecasting;

    constructor(@inject(TYPES.IWeatherForecasting) weatherForecasting: IWeatherForecasting) {
        this.weatherForecasting = weatherForecasting;
    }

    getForecast(): number {
        return this.weatherForecasting.getTemperature();
    }
}

const container = new Container();
container.bind<IWeatherForecasting>(TYPES.IWeatherForecasting).to(WeatherSatellite);
const person = container.resolve(Person);

console.log(person.getForecast()); // Ausgabe: 20

Erklärung

  • @injectable: Dekorator, der die Klasse für Dependency Injection verfügbar macht.
  • @inject: Ermöglicht die Injektion der Abhängigkeiten in den Konstruktor der Klasse.
  • Container: Definiert, welche Implementierungen für die jeweiligen Abhängigkeiten verwendet werden.

UML-Diagramm: Design mit Dependency Injection

UML Diagramm: Dependency Container
UML Diagramm: Dependency Container

Vorteil

Die Person-Klasse ist nun vollständig unabhängig von der Implementierung der Wettervorhersage. Änderungen an der Implementierung können einfach im DI-Container vorgenommen werden.

Erweiterung des DI-Containers

Es ist auch möglich, andere Implementierungen oder Abhängigkeiten zum Container hinzuzufügen, indem man verschiedene Bindungen mit .to() verwendet.

Häufig gestellte Fragen zur Dependency Injection

Warum sollte ich InversifyJS statt manueller Dependency Injection verwenden?

InversifyJS vereinfacht die Verwaltung von Abhängigkeiten und macht den Code modularer. Manuelles Verwalten von Abhängigkeiten kann zu Fehlern und schwer wartbarem Code führen, insbesondere in größeren Anwendungen.

Kann InversifyJS auch in Frontend-Frameworks wie Angular verwendet werden?

Ja, InversifyJS kann sowohl im Backend (Node.js) als auch im Frontend mit Frameworks wie Angular oder React verwendet werden. Es ist jedoch wichtig zu prüfen, ob der gewählte DI-Ansatz gut in das jeweilige Framework integriert werden kann.

Fazit

Dependency Injection in TypeScript mit der Bibliothek InversifyJS hilft dabei, den Code modularer, flexibler und wartungsfreundlicher zu gestalten. InversifyJS ermöglicht eine einfache Verwaltung der Abhängigkeiten, wodurch sich der Code leichter anpassen und erweitern lässt.

CookieFirst Logo

Wir verwenden Cookies

Wir können diese zur Analyse unserer Besucherdaten platzieren, um unsere Webseite zu verbessern, personalisierte Inhalte anzuzeigen und Ihnen ein großartiges Webseiten-Erlebnis zu bieten. Für weitere Informationen zu den von uns verwendeten Cookies öffnen Sie die Einstellungen.