Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Các cách triển khai Dependency Injection và ưu/nhược điểm trong C#

Dependency Injection (DI) là pattern cung cấp dependency cho object từ bên ngoài thay vì object tự tạo (new). Giảm coupling, tăng testability, maintainability.

4 Loại DI chính

1. Constructor Injection (Khuyến nghị nhất)

public class OrderService
{
    private readonly IRepository _repo;
    private readonly ILogger _logger;

    public OrderService(IRepository repo, ILogger logger) // Required dependencies
    {
        _repo = repo ?? throw new ArgumentNullException(nameof(repo));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
}

Ưu: Bắt buộc, bất biến, rõ ràng dependencies, dễ test, hỗ trợ DI container tự động.
Nhược: Constructor quá dài nếu nhiều dependency (code smell -> tách class).

2. Setter/Property Injection

public class ReportGenerator
{
    public IExporter Exporter { get; set; } // Optional dependency
    
    public void Generate() => Exporter?.Export();
}

Ưu: Optional, có thể thay đổi runtime, hỗ trợ serialization/UI framework.
Nhược: Temporal coupling (gọi method trước khi set -> NullRef), ẩn dependency.

3. Interface Injection

public interface ILoggerSetter { void SetLogger(ILogger logger); }

public class Service : ILoggerSetter
{
    private ILogger _logger;
    public void SetLogger(ILogger logger) => _logger = logger;
}

Ưu: Ép consumer implement interface setter rõ ràng.
Nhược: Phức tạp, ít dùng, coupling với interface setter.

4. Service Locator (Anti-pattern theo nhiều người)

public class LegacyController
{
    public void Action()
    {
        var service = ServiceLocator.Current.GetService<IService>(); // Lookup
        service.DoWork();
    }
}

Nhược: Ẩn dependency (không thấy trong constructor), khó test, coupling với container, vi phạm DIP.

So sánh tổng quan

LoạiBắt buộc?Thay đổi runtime?Dễ test?Khuyến nghị
ConstructorCóKhóRất dễ⭐⭐⭐⭐⭐ Mặc định
Property/SetterKhôngDễTrung bìnhOptional dependencies
Interface InjectionCó (qua interface)DễTrung bìnhHiếm dùng
Service LocatorKhông (ẩn)DễKhóTránh (legacy only)

Lợi ích chung của DI

  • Giảm coupling: Class không biết concrete implementation.
  • Tăng reusability: Dễ swap implementation.
  • Cải thiện maintainability: Thay đổi implementation không ảnh hưởng consumer.
  • Cải thiện testing: Dễ mock/stub dependency.
  • Centralized configuration: Đăng ký dependency ở một nơi (Composition Root).

Nhược điểm chung

  • Phức tạp hóa object graph (quá nhiều interface, class nhỏ).
  • Khó debug (stack trace qua container).
  • Performance overhead nhẹ (container resolution).
  • Circular dependency dễ xảy ra nếu không cẩn thận.

Best Practice trong .NET Core

// Program.cs / Startup.cs
builder.Services.AddScoped<IRepository, SqlRepository>();
builder.Services.AddTransient<IEmailService, SmtpEmailService>();
builder.Services.AddSingleton<ICache, RedisCache>();

// Controller - Constructor Injection
public class OrdersController : ControllerBase
{
    private readonly IRepository _repo;
    public OrdersController(IRepository repo) => _repo = repo;
}