Phụ thuộc (dependency) là đối tượng mà đối tượng khác phụ thuộc vào nó. Dependency Injection (DI) - tiêm phụ thuộc (hoặc đảo ngược điều khiển) cơ bản là cung cấp các đối tượng mà một đối tượng cần, thay vì để nó tự tạo ra các đối tượng đó. Đây là kỹ thuật hữu ích làm cho việc kiểm thử (testing) dễ dàng hơn, vì nó cho phép bạn mock (giả lập) các phụ thuộc.
Ví dụ: Lớp A gọi phương thức của lớp B, lớp B gọi phương thức của lớp C → A phụ thuộc B, B phụ thuộc C. Với DI, ta truyền instance của C vào B, instance của B vào A, thay vì để các lớp tự tạo instance.
Trong ví dụ dưới, lớp Runner phụ thuộc Logger. Runner tạo instance Logger trong constructor. Vấn đề:
- Gắn kết chặt
LoggervàoRunner, không thể thay thế mà không sửaRunner. - Nếu
Loggercó phụ thuộc,Runnerphải cấu hình chúng. - Khó test: nếu
Loggertốn tài nguyên (mạng, file), làm chậm test, không dễ thay thế.
using System;
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
}
}
class Runner
{
private Logger _logger;
public Runner()
{
_logger = new Logger();
}
public void Run()
{
_logger.Log("Message to be logged");
}
}
class Logger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
Sử dụng DI: sửa constructor Runner nhận interface ILogger thay vì đối tượng cụ thể. Logger implement ILogger. Điều này cho phép truyền instance Logger vào constructor Runner. Khi test, có thể tạo TestLogger implement ILogger và truyền vào.
Ví dụ với DI
using System;
class Program
{
static void Main(string[] args)
{
var logger = new Logger();
var runner = new Runner(logger);
runner.Run();
}
}
class Runner
{
private ILogger _logger;
public Runner(ILogger logger)
{
_logger = logger;
}
public void Run()
{
_logger.Log("Message to be logged");
}
}
interface ILogger
{
void Log(string message);
}
class Logger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
Kết quả đầu ra
Message to be logged