依赖注入是什么
Dependency Injection is the idea that your components (usually structs in go) should receive their dependencies when being created.
在 Golang 中,构造一个结构体常见的有两种方式:
- 在结构体初始化过程中,构建它的依赖;
- 将依赖作为构造器入参,传入进来。
所谓依赖注入就是第二种思想。不夸张的说,依赖注入是保持我们的软件系统松耦合,可维护的最重要的设计原则。
为什么?
因为当你的依赖通过入参传入,意味着从本对象的角度,你不用去关心它的生成,只用关心它的能力。更具体来讲,它能让我们更加倾向于定义好接口,以接口方法来进行交互。而不是依赖一个具体的实现。
由此而来的另一个好处在于测试。由于依赖是传入的,你的系统只管用它的能力,那么具体这个能力如何实现,其实是由上层来控制的。我们就可以很方便地进行 mock,调整各个场景下依赖的实现,来验证我们的 SUT 的表现。
开源选型
Golang 社区中实现依赖注入的框架有很多,常用的主要是 google/wire, facebook/inject, uber/dig, uber/fx 等,我们这个专栏此前就介绍过 goioc/di,大家感兴趣的话可以往前翻一下。
大体上看,分为两个派系:
- 代码生成 codegen
- 基于反射 reflect
其实不光是 DI 工具,针对 Golang 这种强类型,但泛型能力较弱的语言,包括 copier,orm 这类通用框架都会倾向于在这两个路径上二选一。
同样的,DI 也存在这两个排序,上面我们列举的选项中,facebook/inject, uber/dig, uber/fx,以及我们此前介绍的 goioc/di 都采用了基于反射的解法。这样的好处在于使用起来相对直接,不需要额外生成代码。但劣势也是相对的,失去了编译器检查的能力,如果注入有问题,只能在运行时报错,启动时会存在一些性能消耗。
google/wire 是 Google 官方提出的解决方案,也是业界目前最经典的基于 codegen 来解决依赖注入的开源库。相较于反射这种在运行时搞事情的操作,wire 需要开发者提前使用代码生成工具,触发依赖注入代码的生成,在编译器干活。相对的,会稍微麻烦点,但语义更清晰,也消除了运行时的成本。
今天我们就来