写代码时,你有没有试过改一个类,结果发现要连带改七八个其他类?或者测试某个业务逻辑,得先手动创建一堆对象、配好各种参数,光初始化就写了半屏?这些头疼事,依赖注入(DI)其实早就能帮你扛了。
场景一:换数据库不改业务代码
比如你做用户注册功能,一开始用 MySQL 存数据。后来公司要求支持 PostgreSQL,或者临时想用内存数据库跑单元测试。没用 DI 时,UserRepository 类里直接 new 了个 MySqlConnection,整个项目到处搜着改;用了 DI,你只管定义一个 IUserRepository 接口,让容器在启动时决定塞进去 MySQL 实现还是 PostgreSql 实现:
services.AddScoped<IUserRepository, MySqlUserRepository>();
// 测试时换成:
// services.AddScoped<IUserRepository, InMemoryUserRepository>();业务层完全不用动一行。
场景二:接口升级,多个实现自由切换
支付模块要支持微信、支付宝、银联三种渠道。每个渠道的调用方式、验签逻辑都不同。不用 DI,业务代码里可能满是 if-else 或 switch,一加新渠道就得改主流程;用了 DI,你可以按规则注入对应实现:
public class PaymentService
{
private readonly IPaymentGateway _gateway;
public PaymentService(IPaymentGateway gateway) => _gateway = gateway;
public void Process(Order order) => _gateway.Charge(order);
}运行时根据订单类型或配置,自动注入 WechatGateway 或 AlipayGateway —— 新增渠道只要注册新实现,不碰老代码。
场景三:单元测试不再“硬编码”依赖
测试用户登录,你不想真连数据库、发邮件、调第三方 API。用 DI 后,测试时把真实 EmailSender 换成 MockEmailSender,把 UserRepository 换成 FakeUserRepo,几行 setup 就搞定:
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(x => x.FindById(123)).Returns(new User { Name = "张三" });
var service = new LoginService(mockRepo.Object, new MockEmailSender());
var result = service.Login("123", "pwd");测试快、稳、可重复,还不怕污染测试环境。
场景四:多环境配置差异自动适配
开发时用本地 Redis 缓存,测试环境用集群,生产走云服务。这些连接字符串、超时设置、重试策略全都不一样。DI 容器配合配置系统,能按环境加载不同实现:
if (env.IsDevelopment())
{
services.AddSingleton<ICacheService, LocalCacheService>();
}
else if (env.IsProduction())
{
services.AddSingleton<ICacheService, RedisCloudCacheService>();
}主业务代码永远只认 ICacheService,环境一变,背后实现悄悄换,毫无感知。
场景五:插件式功能,运行时动态加载
后台管理系统要支持客户自定义导出格式:Excel、PDF、CSV。你把每种导出器做成独立类,实现 IExportHandler 接口,再统一注册到容器:
services.AddTransient<IExportHandler, ExcelExportHandler>();
services.AddTransient<IExportHandler, PdfExportHandler>();
services.AddTransient<IExportHandler, CsvExportHandler>();前端选哪种格式,后端就从容器里 resolve 对应实现,甚至还能配合反射+配置,把新导出器打包成 DLL 放进插件目录,重启即生效 —— 不用重新编译主程序。
依赖注入不是炫技,它是帮你在需求变来变去、环境换来换去、测试越来越严的日常里,少写几行胶水代码,多留点精力解决真问题。