依赖注入 Dependency Injection
当A类使用B类的某些功能时,则表示A类具有B类的依赖,然而,如果在A类内部实例化B类,会导致A类与B类耦合,导致程序不易维护。
例如小明类内部实例化了一个手机类,当手机需要改变时还需要改造小明。
再例如:
这里 Worker
类依赖 MessageWrite
类,直接在 Worker 类里实例化了 MessageWrite 类(硬编码)。
public class Worker : BackgroundService
{
private readonly MessageWriter _messageWriter = new MessageWriter();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1000, stoppingToken);
}
}
}
导致:
- 如果 MessageWrite 的实现发生变化, Worker 类也必须修改。
- 如果 MessageWrite 有依赖项或者参数,也需要在 Worker 类里进行配置。
- 耦合太高,无法单元测试。
为了解决这个问题,需要将A类对B类的控制权抽离出来,交给一个第三方。这就是控制反转(Inversion Of Control)。 控制反转是一种思想,依赖注入(Dependency Injection)是一种实现方法。通过第三方,把B类的构造函数注入到A类里,让A类直接使用, 不需要实例化B类。
代码实现
定义一个 IMessageWriter 接口,在接口中实现 Write 方法。
namespace DependencyInjection.Example;
public interface IMessageWriter
{
void Write(string message);
}
然后通过 MessageWriter 实现这个接口。
namespace DependencyInjection.Example;
public class MessageWriter : IMessageWriter
{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
在 Main 程序中注册:
namespace DependencyInjection.Example;
class Program
{
static Task Main(string[] args) =>
CreateHostBuilder(args).Build().RunAsync();
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddHostedService<Worker>()
.AddScoped<IMessageWriter, MessageWriter>());
}
最后修改 Worker 类使用这个接口:
namespace DependencyInjection.Example;
public class Worker : BackgroundService
{
private readonly IMessageWriter _messageWriter;
public Worker(IMessageWriter messageWriter) =>
_messageWriter = messageWriter;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1000, stoppingToken);
}
}
}