Menu
Coddy logo textTech

Podstawy wstrzykiwania zależności

Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 46 z 70.

Wstrzykiwanie zależności (DI) to technika, w której klasa otrzymuje swoje zależności z zewnątrz, zamiast tworzyć je samodzielnie. Dzięki temu kod jest bardziej elastyczny, łatwiejszy do testowania i słabiej powiązany.

Bez DI klasa tworzy własne zależności, przez co trudno ją zmienić lub testować:

public class OrderService
{
    private EmailSender sender = new EmailSender();  // Silnie powiązane
    
    public void PlaceOrder() => sender.Send("Order placed");
}

W przypadku DI zależności są przekazywane przez konstruktor:

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");
}

Teraz OrderService nie wie ani nie musi wiedzieć, czy korzysta z poczty e-mail, SMS-ów czy atrapowego nadawcy do testowania. Zależność jest „wstrzykiwana” podczas tworzenia obiektu:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // Email: Zamówienie złożone

Ten wzorzec opiera się na zasadzie zależności od abstrakcji (interfejsów), a nie od konkretnych implementacji. Umożliwia wymianę implementacji bez modyfikowania klasy, która z nich korzysta, co jest niezbędne do testów jednostkowych i utrzymywania elastycznej architektury.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system logowania, który pokaże możliwości wstrzykiwania zależności. Zamiast na stałe zakładać, w jaki sposób zapisywane są logi, utworzysz elastyczną klasę Logger, która może współpracować z dowolnym miejscem docelowym logów — konsolą, symulacją pliku lub czymkolwiek innym — nie znając szczegółów.

Podzielisz kod na trzy pliki:

  • ILogWriter.cs: Zdefiniuj interfejs o nazwie ILogWriter w przestrzeni nazw Logging. Interfejs ten reprezentuje kontrakt dla dowolnego miejsca docelowego logów i powinien zawierać jedną metodę Write(string message), która zwraca ciąg znaków opisujący, gdzie zapisano wiadomość.
  • LogWriters.cs: Utwórz dwie konkretne implementacje interfejsu ILogWriter w przestrzeni nazw Logging:
    • ConsoleLogWriter — jego metoda Write zwraca Console: {message}
    • FileLogWriter — przyjmuje nazwę pliku w konstruktorze, a jego metoda Write zwraca File[{filename}]: {message}
  • Logger.cs: Utwórz klasę Logger w przestrzeni nazw Logging, która otrzymuje swoją zależność przez wstrzyknięcie w konstruktorze. Klasa Logger powinna:
    • Przyjmować ILogWriter przez konstruktor i przechowywać go w prywatnym polu
    • Zawierać metodę Log(string message), która przekazuje wywołanie do wstrzykniętego obiektu zapisującego i zwraca wynik
  • Program.cs: Zademonstruj wstrzykiwanie zależności, tworząc dwa różne obiekty rejestrujące — jeden z ConsoleLogWriter, a drugi z FileLogWriter. Pokaż, jak ta sama klasa Logger zachowuje się różnie w zależności od wstrzykniętej zależności.

Otrzymasz dwa dane wejściowe:

  • Nazwę pliku dla rejestratora zapisującego do pliku
  • Wiadomość do zapisania w logu

Utwórz obiekt Logger z ConsoleLogWriter i zapisz wiadomość w logu. Następnie utwórz kolejny obiekt Logger z FileLogWriter (używając nazwy pliku z danych wejściowych) i zapisz tę samą wiadomość w logu. Wypisz każdy wynik w osobnym wierszu.

Na przykład, jeśli danymi wejściowymi są app.log i System started, wynik powinien wyglądać następująco:

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

Zwróć uwagę, że klasa Logger nigdy się nie zmienia — współpracuje z dowolną implementacją ILogWriter. Na tym polega istota wstrzykiwania zależności: klasa Logger zależy od abstrakcji (interfejsu), a konkretna implementacja jest „wstrzykiwana” z zewnątrz. Później możesz z łatwością dodać DatabaseLogWriter lub CloudLogWriter, nie modyfikując w ogóle klasy Logger!

Spróbuj swoich sił

using System;
using Logging;

class Program
{
    public static void Main(string[] args)
    {
        // Odczytaj dane wejściowe
        string filename = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Utwórz Logger z ConsoleLogWriter i zapisz komunikat w dzienniku
        // Wyświetl wynik

        // TODO: Utwórz kolejny Logger z FileLogWriter (używając filename) i zapisz ten sam komunikat w dzienniku
        // Wyświetl wynik
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C# online