Dependency Injection w .NET- Refaktoryzacja do Clean Architecture [5/6] [Część 10]
Czy Twój plik Program.cs pęka w szwach od ręcznego tworzenia obiektów? 🚀
Dependency Injection (DI) w .NET to klucz do czystej architektury i profesjonalnego kodu. Dzięki DI Twój kod staje się prostszy, testowalny i gotowy na rozwój – bez nadmiaru new, bez spaghetti code. W tym wpisie pokażę Ci, jak wdrożyć DI w aplikacji konsolowej .NET krok po kroku.
Dependency Injection w .NET – co to jest i dlaczego warto?
Dependency Injection (DI) to wzorzec projektowy, który odwraca kontrolę tworzenia obiektów.
Zamiast samemu tworzyć instancje klas, pozwalamy, aby zrobił to za nas kontener DI.
Najważniejsze korzyści DI:
- ✅ Eliminacja tight coupling (luźne powiązania między klasami)
- ✅ Łatwiejsze testowanie i mockowanie zależności
- ✅ Separation of Concerns – każda klasa ma tylko jedną odpowiedzialność
- ✅ Centralna konfiguracja zależności w jednym miejscu
- ✅ Profesjonalne podejście zgodne z zasadami Clean Architecture
📌 Zobacz też: kurs poświęcony typom generycznym w C# to solidna dawka wiedzy, która pozwoli Ci tworzyć bardziej elastyczny, efektywny i wielokrotnego użytku kod. Nauczysz się, jak korzystać z generyków na różnych poziomach – od podstawowych po zaawansowane koncepcje, takie jak budowanie kontenera wstrzykiwania zależności (Dependency Injection Container)
Krok 1. Instalacja pakietów NuGet
Do wdrożenia Dependency Injection w aplikacji konsolowej potrzebujemy dwóch pakietów:
<!-- Infrastructure project -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<!-- ConsoleApp project -->
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
- Microsoft.Extensions.DependencyInjection – konfiguracja serwisów
- Microsoft.Extensions.Hosting – obsługa cyklu życia aplikacji
Dzięki temu zachowujemy Separation of Concerns: Infrastructure konfiguruje serwisy, a ConsoleApp zarządza uruchomieniem.
Krok 2. Extension Method dla konfiguracji serwisów
Zamiast rejestrować serwisy w wielu miejscach, lepiej stworzyć jedną metodę rozszerzającą.
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddRpgServices(this IServiceCollection services)
{
// Core Services
services.AddTransient<IBattleService, BattleService>();
// Application Services
services.AddScoped<IGameService, GameService>();
// Infrastructure Services
services.AddSingleton<IPostacFactory, PostacFactory>();
services.AddTransient<IPostacRepository, JsonPostacRepository>();
return services;
}
}
Wyjaśnienie lifetime’ów serwisów:
- Transient – nowa instancja przy każdym wywołaniu (
BattleService
,JsonPostacRepository
) - Scoped – jedna instancja na sesję (
GameService
) - Singleton – jedna instancja w całej aplikacji (
PostacFactory
)
Krok 3. Refaktoryzacja Program.cs z HostBuilder
Przed refaktoryzacją Program.cs
wyglądał tak:
var factory = new PostacFactory();
var repository = new JsonPostacRepository(factory);
var battleService = new BattleService();
var gameService = new GameService(repository, battleService);
Po refaktoryzacji z DI wystarczy:
using var scope = host.Services.CreateScope();
var gameService = scope.ServiceProvider.GetRequiredService<IGameService>();
A konfiguracja HostBuildera:
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddRpgServices();
});
Dzięki temu kod jest czystszy, a Program.cs zajmuje się tylko uruchamianiem aplikacji.
Dlaczego warto stosować Dependency Injection w .NET?
1. Testowalność
DI pozwala podmienić implementacje na mocki w testach jednostkowych.
2. Łatwe rozszerzenia
Dodanie nowego serwisu = 1 linijka w AddRpgServices
.
3. Profesjonalne praktyki
Wykorzystujesz standardowe narzędzia .NET (HostBuilder
, ServiceCollection
).
4. Czysty kod
Brak ręcznego tworzenia obiektów, brak cyklicznych zależności, większa czytelność.
Podsumowanie
Po tej refaktoryzacji Twój kod:
- ✅ działa w pełni z Dependency Injection
- ✅ ma centralną konfigurację serwisów
- ✅ jest testowalny i rozszerzalny
- ✅ realizuje zasady Inversion of Control i Clean Architecture
To ogromny krok w stronę profesjonalnego programowania w C#.
👉 W kolejnym odcinku serii pokażę, jak wydzielić GameRunner.
Podobał Ci się ten artykuł?
💬 Zostaw komentarz poniżej – jakie wzorce projektowe najczęściej stosujesz w swoich projektach?
📹 Sprawdź też pełną serię na YouTube: Gra RPG – [C# OOP Kurs]
👉 subskrybuj kanał DevHobby i nie przegap kolejnych odcinków