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żoneTen 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.
Wyzwanie
ŁatwyZbudujmy 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 nazwieILogWriterw przestrzeni nazwLogging. 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 interfejsuILogWriterw przestrzeni nazwLogging:ConsoleLogWriter— jego metodaWritezwracaConsole: {message}FileLogWriter— przyjmuje nazwę pliku w konstruktorze, a jego metodaWritezwracaFile[{filename}]: {message}
Logger.cs: Utwórz klasęLoggerw przestrzeni nazwLogging, która otrzymuje swoją zależność przez wstrzyknięcie w konstruktorze. KlasaLoggerpowinna:- Przyjmować
ILogWriterprzez 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
- Przyjmować
Program.cs: Zademonstruj wstrzykiwanie zależności, tworząc dwa różne obiekty rejestrujące — jeden zConsoleLogWriter, a drugi zFileLogWriter. Pokaż, jak ta sama klasaLoggerzachowuje 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 startedZwróć 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
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online