依赖注入概念与优势:DI 到底是什么?

依赖注入,简称 DI,很多人第一反应就是“那不就是 Dagger 吗?”
嗯,我刚入行时也这么想。后来才发现,DI 其实是一种设计思想,Dagger、Hilt、Koin 只是工具。
说白了,DI 就是“把对象的创建权交出去”。

举个生活中的例子
你自己去超市买米做饭,这叫手动依赖。你打电话叫外卖,米和菜由商家搞定,这就是依赖注入。
你的程序也一样——类不该自己去 new 依赖,而是等着别人把依赖送进来。

为什么需要 DI?

我在项目中遇到过这样一个场景:一个网络请求模块,直接 new 了 OkHttpClient。后来要换成 Retrofit,改代码改到崩溃。
你想想看,如果依赖是外部注入的,换起来就简单多了。

DI 的核心价值在于:

  • 松耦合:类不关心依赖的具体实现,只关心接口
  • 可测试性:测试时可以轻松注入 Mock 对象
  • 复用性:换个实现换个环境,代码不用动
  • 可维护性:改一处依赖,所有地方都跟着变

记住一句话:高内聚,低耦合。DI 就是低耦合的最佳实践。

控制反转(IoC)到底怎么理解?

控制反转是 DI 的理论基础。说白了,就是“谁控制谁”的问题。
传统写法:你控制依赖的创建。DI 写法:框架或容器控制依赖的创建。

我刚开始学的时候,一直搞不清 IoC 和 DI 的区别。
后来一位前辈一句话点醒了我:“IoC 是思想,DI 是手段。”

举个例子:

// 传统写法:主动创建
public class UserService {
    private Database database = new MySQLDatabase();
}

// DI 写法:被动接收
public class UserService {
    private Database database;
    
    public UserService(Database database) {
        this.database = database;
    }
}

看到了吗?控制权从 UserService 转移到了外部。这就是控制反转。

手动注入 vs 框架注入

很多人纠结:直接用构造方法传参不就行了?干嘛非要用 Dagger?
我来给你拆解一下。

对比维度 手动注入 框架注入
代码量 少,但重复 多,但一次配置
可读性 直接,不用猜 需要学习成本
可维护性 依赖链一长就崩 自动管理依赖链
学习成本 零 高(Dagger/Hilt)
适用范围 小项目或简单模块 大型多模块项目

我个人习惯:

  • 小工具类或测试辅助类,手动注入足够
  • 多模块项目,尤其涉及回归测试,一定用框架注入

避坑指南:我曾经在一个 20+ 模块的项目里用手动注入,结果改一个依赖,要改十几个构造方法。后来切换到 Hilt,才解脱。

注意:框架注入不是银弹。如果你用的是 Dagger,编译时间会变长。权衡好利弊。

依赖注入核心概念结构图 依赖注入 DI 为什么需要 DI 松耦合 可测试性 复用性 可维护性 控制反转 IoC 思想:谁控制谁 传统 vs 框架 手动注入 框架注入 构造方法传参 Setter 注入 Dagger/Hilt Koin 选择建议:小项目手动,多模块项目用框架 尤其是回归测试场景,框架注入让 Mock 变得更简单

总结一下:
DI 不是某个框架的专利。它是一种思维方式。
记得我在重构旧项目时,把 new 对象的地方全部改成注入,测试写起来顺手多了。
你想想看,如果每个模块都能随时替换依赖,回归测试还怕什么?