Menu
Coddy logo textTech

Grundlagen der Dependency Injection

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 46 von 70.

Dependency Injection (DI) ist eine Technik, bei der eine Klasse ihre Abhängigkeiten von außen erhält, anstatt sie selbst zu erstellen. Dadurch wird der Code flexibler, besser testbar und lose gekoppelt.

Ohne DI erstellt eine Klasse ihre eigenen Abhängigkeiten, wodurch Änderungen und Tests erschwert werden:

public class OrderService
{
    private EmailSender sender = new EmailSender();  // Eng gekoppelt
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Bei DI werden Abhängigkeiten über den Konstruktor übergeben:

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

Jetzt weiß OrderService nicht und kümmert sich auch nicht darum, ob es E-Mail, SMS oder einen Mock-Sender zum Testen verwendet. Die Abhängigkeit wird beim Erstellen des Objekts „injiziert“:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // E-Mail: Bestellung aufgegeben

Dieses Muster folgt dem Prinzip, von Abstraktionen (Schnittstellen) statt von konkreten Implementierungen abzuhängen. Es ermöglicht das Austauschen von Implementierungen, ohne die Klasse zu ändern, die sie verwendet, was für Unit-Tests und die Aufrechterhaltung flexibler Architekturen unerlässlich ist.

challenge icon

Aufgabe

Einfach

Wir erstellen ein Protokollierungssystem, das die Leistungsfähigkeit von Dependency Injection demonstriert. Statt fest zu codieren, wie Protokolle geschrieben werden, erstellst du eine flexible Logger-Klasse, die mit jedem Protokollierungsziel – Konsole, Dateisimulation oder etwas anderem – arbeiten kann, ohne die Einzelheiten zu kennen.

Du organisierst deinen Code über drei Dateien:

  • ILogWriter.cs: Definiere im Namespace ILogWriter ein Interface namens Logging. Dieses Interface stellt den Vertrag für jedes Protokollierungsziel dar und sollte eine einzige Methode Write(string message) besitzen, die einen String zurückgibt, der beschreibt, wohin die Nachricht geschrieben wurde.
  • LogWriters.cs: Erstelle im Namespace ILogWriter zwei konkrete Implementierungen von Logging:
    • ConsoleLogWriter – die Methode Write gibt Console: {message} zurück
    • FileLogWriter – erhält seinen Konstruktor über einen Dateinamen, und seine Methode Write gibt File[{filename}]: {message} zurück
  • Logger.cs: Erstelle im Namespace Logger eine Logging-Klasse, die ihre Abhängigkeit über Konstruktorinjektion erhält. Der Logger sollte:
    • Über seinen Konstruktor ein ILogWriter akzeptieren und es in einem privaten Feld speichern
    • Eine Methode Log(string message) besitzen, die die Aufgabe an den injizierten Writer delegiert und das Ergebnis zurückgibt
  • Program.cs: Demonstriere Dependency Injection, indem du zwei verschiedene Logger erstellst – einen mit einem ConsoleLogWriter und einen mit einem FileLogWriter. Zeige, wie sich dieselbe Logger-Klasse abhängig davon, welche Abhängigkeit injiziert wird, unterschiedlich verhält.

Du erhältst zwei Eingaben:

  • Den Dateinamen für den Datei-Logger
  • Die zu protokollierende Nachricht

Erstelle einen Logger mit einem ConsoleLogWriter und protokolliere die Nachricht. Erstelle anschließend einen weiteren Logger mit einem FileLogWriter (unter Verwendung des Dateinamens aus der Eingabe) und protokolliere dieselbe Nachricht. Gib jedes Ergebnis in einer eigenen Zeile aus.

Wenn die Eingaben beispielsweise app.log und System started lauten, sollte die Ausgabe folgendermaßen aussehen:

Console: System started
File[app.log]: System started

Beachte, dass sich die Logger-Klasse niemals ändert – sie funktioniert mit jeder ILogWriter-Implementierung. Das ist das Wesentliche von Dependency Injection: Der Logger hängt von einer Abstraktion (dem Interface) ab, und die konkrete Implementierung wird von außen „injiziert“. Später könntest du problemlos einen DatabaseLogWriter oder CloudLogWriter hinzufügen, ohne die Logger-Klasse überhaupt zu ändern!

Probier es selbst

using System;
using Logging;

class Program
{
    public static void Main(string[] args)
    {
        // Eingaben lesen
        string filename = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Erstelle einen Logger mit einem ConsoleLogWriter und protokolliere die Nachricht
        // Gib das Ergebnis aus

        // TODO: Erstelle einen weiteren Logger mit einem FileLogWriter (unter Verwendung von filename) und protokolliere dieselbe Nachricht
        // Gib das Ergebnis aus
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C#-Compiler