【设计模式】依赖注入(Dependency Injection, DI)

依赖注入(Dependency Injection, DI)是一种软件设计模式,用于实现对象之间的松耦合,同时提升代码的可测试性和可维护性。它主要用于管理和提供对象的依赖关系,而不需要在代码中手动创建依赖实例。

核心概念

  • 依赖 :一个类需要的服务或对象。例如,类 A 需要类 B 提供某些功能,那么 B 就是 A 的依赖。
  • 注入:将依赖通过外部方式传递给类,而不是让类直接创建或获取这些依赖。

在传统编程中,如果类 A 需要使用类 B,通常会在 A 中直接创建一个 B 的实例。这种方式称为"紧耦合",会导致以下问题:

  1. 难以测试 :无法轻松替换 B 的实现进行单元测试。
  2. 难以扩展 :修改 B 的实现可能需要更改 A 的代码。
  3. 依赖管理困难:多个类之间的依赖关系可能变得复杂且难以维护。

依赖注入通过将依赖的创建工作交给外部框架或容器解决了这些问题。


实现依赖注入的方法

  1. 构造函数注入(Constructor Injection) 在构造函数中通过参数传递依赖对象。

    复制代码
    public class A
    {
        private readonly B _b;
    
        public A(B b)
        {
            _b = b;
        }
    
        public void DoSomething()
        {
            _b.SomeMethod();
        }
    }
  2. 属性注入(Property Injection) 通过公共属性设置依赖。

    复制代码
    public class A
    {
        public B B { get; set; }
    
        public void DoSomething()
        {
            B?.SomeMethod();
        }
    }
  3. 方法注入(Method Injection) 通过方法参数传递依赖。

    复制代码
    public class A
    {
        public void DoSomething(B b)
        {
            b.SomeMethod();
        }
    }

DI 容器的作用

在 C# 中,依赖注入通常结合 DI 容器使用 ,比如 Microsoft 提供的 ASP.NET Core 自带 DI 容器 或第三方库(如 Autofac、Ninject)。DI 容器的主要功能是:

  1. 管理依赖关系。
  2. 自动创建对象并注入其依赖。
  3. 提供对象的生命周期管理(例如单例、瞬态、作用域)。

以下是 ASP.NET Core 的 DI 示例:

复制代码
// 服务接口
public interface IService
{
    void Serve();
}

// 服务实现
public class Service : IService
{
    public void Serve()
    {
        Console.WriteLine("Service Called");
    }
}

// 使用服务的控制器
public class Controller
{
    private readonly IService _service;

    public Controller(IService service)
    {
        _service = service;
    }

    public void Action()
    {
        _service.Serve();
    }
}

// 程序入口
class Program
{
    static void Main(string[] args)
    {
        // 创建 DI 容器
        var serviceProvider = new ServiceCollection()
            .AddTransient<IService, Service>() // 注册服务
            .BuildServiceProvider();

        // 从容器中获取控制器
        var controller = new Controller(serviceProvider.GetService<IService>());

        controller.Action();
    }
}

优点

  1. 松耦合:对象不直接依赖具体实现,而是依赖于接口或抽象。
  2. 更易测试:通过注入模拟对象(Mocks),轻松进行单元测试。
  3. 可维护性和扩展性更好:更容易修改或扩展应用程序。

适用场景

  • 复杂应用程序:需要管理大量依赖关系时。
  • 单元测试驱动开发(TDD):依赖注入便于注入 mock 对象。
  • 分层架构或微服务:减少模块间的耦合。

通过依赖注入,可以有效改善代码的结构和质量,使开发过程更高效、灵活。

相关推荐
唐青枫43 分钟前
C#.NET Expression Tree 深入解析:表达式树、动态查询与运行时代码生成
c#·.net
saltymilk15 小时前
使用 C++ 模拟 ShaderLanguage 的 swizzle
c++·模板元编程
xlp666hub21 小时前
Leetcode第五题:用C++解决盛最多水的容器问题
linux·c++·leetcode
程序设计实验室1 天前
C# 扩展方法只会写 this 吗?C# 14 新语法直接把扩展方法玩出了花
c#
程序员Terry1 天前
同事被深拷贝坑了3小时,我教他原型模式的正确打开方式
java·设计模式
得物技术1 天前
搜索 C++ 引擎回归能力建设:从自测到工程化准出|得物技术
c++·后端·测试
唐青枫1 天前
C#.NET SignalR 深入解析:实时通信、Hub 与连接管理实战
c#·.net
唐宋元明清21881 天前
.NET Win32磁盘动态卷/跨区卷触发“函数不正确”问题排查
windows·c#·存储
hez20101 天前
Satori GC:同时做到高吞吐、低延时和低内存占用
c#·.net·.net core·gc·clr
刀法如飞2 天前
AI时代,程序员都应该是算法思想工程师
人工智能·设计模式·程序员