控制反转(Inversion of Control, IoC)

1.一些名词概念

  • 依赖倒置原则(Dependency Inversion Principle,DIP):一种软件架构设计的原则(抽象概念)。
  • 控制反转(Inversion of Control, IoC):一种设计原则、用来描述将对象的创建和依赖关系管理从应用程序代码中提取到外部容器或框架中(DIP的具体实现方式)。
  • 依赖注入(Dependency Injection, DI):IoC的一种实现方式,用来反转依赖(IoC的具体实现方式)。
  • IoC容器:依赖注入的框架,用来映射依赖,管理对象创建和生存周期(DI框架)。

2.依赖倒置原则(DIP)

定义:高层模块不应该依赖低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象
举个栗子:

  • 没有遵循依赖倒置原则

假设我们有一个 OrderService 类,它负责处理订单逻辑,而 EmailService 类负责发送电子邮件通知。在没有遵循依赖倒置原则的情况下,OrderService 可能直接依赖于EmailService,如:

public class EmailService
{
    
    
    public void SendEmail(string to, string subject, string body)
    {
    
    
        // 发送电子邮件的具体实现
    }
}

public class OrderService
{
    
    
    private EmailService _emailService;

    public OrderService()
    {
    
    
        _emailService = new EmailService(); // 直接创建具体的 EmailService 实例
    }

    public void ProcessOrder(Order order)
    {
    
    
        // 处理订单逻辑
        _emailService.SendEmail(order.CustomerEmail, "Order Confirmation", "Your order has been processed.");
    }
}

在这种设计中,OrderService EmailService 紧密耦合,OrderService 直接依赖于 EmailService 的实现。这意味着,如果我们需要改变 EmailService 的实现或替换为其他的通知方式(比如短信通知),我们就需要修改 OrderService,从而破坏了系统的可扩展性。

  • 遵循依赖倒置原则

遵循依赖倒置原则后,我们会引入抽象(接口或抽象类),使得 OrderService 不再直接依赖EmailService,而是依赖于一个通知接口,具体的通知方式可以通过依赖注入来实现。代码如下:

// 抽象的通知接口
public interface INotificationService
{
    
    
    void SendNotification(string to, string subject, string body);
}

// EmailService 实现通知接口
public class EmailService : INotificationService
{
    
    
    public void SendNotification(string to, string subject, string body)
    {
    
    
        // 发送电子邮件的具体实现
    }
}

// 订单服务依赖于通知接口
public class OrderService
{
    
    
    private readonly INotificationService _notificationService;

    public OrderService(INotificationService notificationService)
    {
    
    
        _notificationService = notificationService; // 通过构造函数注入依赖
    }

    public void ProcessOrder(Order order)
    {
    
    
        // 处理订单逻辑
        _notificationService.SendNotification(order.CustomerEmail, "Order Confirmation", "Your order has been processed.");
    }
}

在这个设计中,OrderService 不再直接依赖于 EmailService,而是依赖于 INotificationService 接口。EmailService 实现了 INotificationService 接口,而 OrderService 可以通过构造函数注入任何实现了 INotificationService 的服务。因此,如果我们需要替换 EmailService,例如使用 SmsService,我们只需要提供一个新的实现,而不需要修改 OrderService。

3.依赖注入(DI)

依赖注入类型:

  1. 构造函数注入
public class Service
{
    
    
    private readonly IRepository _repository;
    
    public Service(IRepository repository)
    {
    
    
        _repository = repository;
    }
}
  1. 属性注入
public class Service
{
    
    
    public IRepository Repository {
    
     get; set; }
}
  1. 方法注入
public class Service
{
    
    
    public void DoWork(IRepository repository)
    {
    
    
        // 使用 repository
    }
}

4.IoC容器

  • IoC容器核心功能
    • 管理对象的生命周期:容器可以定义对象的生命周期(例如:单例、瞬态、作用域等),决定何时创建对象,如何管理对象
      • 单例模式:在容器中注册的对象在整个应用程序生命周期中只有一个实例,多个地方使用时引用同一个实例。
      • 瞬态模式:每次请求时,容器都会创建一个新的实例。当对象需要频繁创建且每个实例独立时使用
      • 作用域模式:对象的生命周期在一个作用域(如 HTTP 请求)内是唯一的。在同一个作用域内,多个请求会复用同一个实例
        *依赖注入:IoC 容器自动将一个对象所需要的依赖项注入到对象中,而不是由对象自己去创建依赖项

IoC 容器的工作原理
IoC 容器根据预设的配置,将对象和它们的依赖关系通常通过接口)注册到容器中。然后,容器负责根据依赖关系自动创建对象并进行注入。容器充当一个“中介”,负责对象的创建和依赖管理。

  1. 注册
    将类或服务注册到容器中。容器需要知道如何创建一个类的实例,以及如何管理它的生命周期。
  2. 解析
    当需要一个对象时,IoC 容器会根据已注册的信息,解析对象的依赖并创建对象实例,同时注入所有必需的依赖项。
  3. 依赖注入
    当容器解析对象时,它会将所需的依赖注入到目标对象中。依赖注入通常通过构造函数、属性或方法来实现。

常见的 IoC 容器:

  • .NET Core / ASP.NET Core:
    这是 .NET Core 框架中的默认 IoC 容器,支持注册服务、自动依赖注入以及生命周期管理。
  • Autofac:
    一种非常流行的 .NET IoC 容器,提供丰富的功能和高级配置选项,支持模块化的注册和更多的生命周期管理策略。
  • Unity(.NET):
    Unity 容器是另一个用于 .NET 的轻量级 IoC 容器,支持高度可配置的依赖注入。
  • Spring:
    一个用于 Java 的全面的 IoC 容器,广泛用于企业级应用开发。Spring 提供了丰富的功能,如事务管理、面向切面编程(AOP)等。
  • Zenject(Unity):
    一个专为 Unity 设计的 IoC 容器,支持依赖注入,帮助解耦 Unity 游戏开发中的各个模块。
    GitHub地址:https://github.com/modesttree/Zenject/tree/master
    中文翻译博客:https://blog.csdn.net/weixin_43405845/article/details/104344019