依赖注入的基本概念
依赖:一个对象(称为客户端)为了完成其功能,需要另一个对象(称为服务)的帮助。这个需要帮助的对象就是客户端的依赖。
注入:在依赖注入中,“注入”的意思是将服务对象传递给需要它的客户端对象,而不是让客户端自己去创建或寻找服务对象。
为什么使用依赖注入?
降低耦合度:客户端不再直接创建服务对象,而是由外部系统(如依赖注入容器)来创建并注入,这样客户端就不需要知道服务对象的具体实现细节。
提高可测试性:在单元测试中,可以轻松地替换真实的依赖为模拟对象(mocks),使得测试更加简单和可控。
提高灵活性:可以在运行时更换不同的实现,而无需修改客户端代码。
依赖注入的实现方式
依赖注入主要有三种实现方式:
构造函数注入(最推荐):通过构造函数的参数来注入依赖项。这是最常用也是最推荐的方法,因为它能保证依赖项在对象创建时就已经确定,并且不可更改。
属性注入:通过对象的属性来注入依赖项。这种方式相对灵活,但不如构造函数注入安全,因为依赖项可以在对象创建之后被更改。
方法注入:通过方法的参数来注入依赖项。这种方式较少使用,因为它可能导致依赖关系变得模糊不清。
示例:构造函数注入示例(推荐)
下面通过一个简单的示例来说明构造函数依赖注入:
定义接口ILogger
// 定义一个接口ILogger
public interface ILogger
{
void Log(string message);
}
实现接口ConsoleLogger
// 实现接口ConsoleLogger
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"Console Logger: {message}");
}
}
定义一个依赖于 ILogger 的服务Service
// 定义一个依赖于 ILogger 的服务
public class Service
{
private readonly ILogger _logger;
// 构造函数注入
public Service(ILogger logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.Log("Doing work...");
}
}
// 手动创建对象
class Program
{
static void Main()
{
ILogger logger = new ConsoleLogger();
Service service = new Service(logger);
service.DoWork();
}
}
在这个例子中,Service 类依赖于 ILogger 接口。通过构造函数注入,Service 类获得了所需的 ILogger 实例。这样,Service 类不需要关心 ILogger 的具体实现细节,只要知道如何使用 ILogger 接口即可。
其实,依赖注入是代替了new的过程