目录

依赖注入是什么

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 需要开发者提前使用代码生成工具,触发依赖注入代码的生成,在编译器干活。相对的,会稍微麻烦点,但语义更清晰,也消除了运行时的成本。

今天我们就来看看 wire 是怎么用的。

wire

Wire is a code generation tool that automates connecting components using dependency injection. Dependencies between components are represented in Wire as function parameters, encouraging explicit initialization instead of global variables. Because Wire operates without runtime state or reflection, code written to be used with Wire is useful even for hand-written initialization.

wire 在设计上受到了 Java’s Dagger 2 的启发。正如官方对它的定位,wire 是一个 Compile-time Dependency Injection for Go (编译期依赖注入)的代码生成工具。wire 非常的轻量级,只会帮助开发者进行按需初始化。

你甚至可以用手写的初始化代码来替换它,wire 作为一个代码生成工具,仅仅是帮助我们减少注入依赖的繁琐工作。

一个经典的 DI 函数签名类似下面这样: