前言

Nest.js 是一个使用 TypeScript 实现的在 Node.js 环境中运行的 Web 服务开发框架。它借鉴了很多优秀的设计思想,本文来说一说 Nest 中的依赖注入和控制反转。

依赖注入

依赖注入,英文名是 Dependency Injection,简称 DI

什么是依赖注入?可以分开来看,就是“依赖”和“注入”。

您可能想了,你这是在玩组词造句吗?

也是,也不是。有时候将一个概念名词进行拆解,在一定程度上是能够方便理解的。

Nest 采用面向对象编程,而不像经典的 Express 和 Koa 一样,是函数式风格编程。站在面向对象的角度,所谓“依赖”,其实就是写代码时用到的一个一个的类的实例。

假设这里有 3 个类,分别是 A,B,C,但是它们之间会互相引用,产生依赖关系,比如:

// A.ts
import B from 'B.ts'

class A {
    constructor() {
        this.b = new B()
    }
}

// B.ts
import C from 'C.ts'

class B {
    constructor() {
        this.c = new C()
    }
}

// C.ts
import A from 'A.ts'

class C {
    constructor() {
        this.a = new A()
    }
}

上面示例代码中,实例 b 就是 A 类的依赖,实例 c 就是 B 类的依赖,实例 a 就是 C 类的依赖。

当有几十个类、上百个类时,如果人工维护这种依赖关系,将会非常复杂和繁琐。

为了解决这个问题,可以采用一种叫做控制反转的设计原则。

控制反转

控制反转也就是 IoC,英文名唤作 Inversion of Control

同样对它进行拆解,就是“控制” 和 “反转”。

还是以上面的代码为例,在 A 类中要用到依赖项 b,所以我们自己在构造方法中实例化了 B类。这个过程,就是控制。我们自己控制了B类的引入,以及实例 b的创建。这也是我们平时开发中最正常,最普通的一种方式。

原本由我们自己控制的流程,交给“其他人”去负责,也就是将控制权进行了转移,这个就叫做“反转”。

那控制权转移给谁了呢?现在你面前只有两样东西:一个是你自己,一个是电脑里的代码,更准确的说,是你正在使用的框架 Nest.js。控制权不在你这里了,那么自然只能跑到框架中去了,总不至于会跑到隔壁同事身上去。

啰嗦了这么一大堆,其实就是一句话:

把原本我该负责的工作(创建依赖),交给框架自己去负责(它需要哪个依赖,就自己去引入),这就是控制反转 IoC。

这时候您又说了:

诶,不对啊,你只说了“依赖”、“控制”和“反转”,还有“注入”没说呢?

其实上面那句话,已经包含了“注入”的过程。控制反转的时候,依赖由框架负责引入,注入到需要它的地方,就是注入。

DI 容器

按照上面所讲述的过程:创建和维护依赖很繁琐,于是交给框架去做。

框架中的具体“负责人”叫做 DI 容器,即依赖注入容器,它具体负责了哪些事情呢:当应用启动时,DI 容器会把依赖都扫描出来,注册到容器中,等代码中哪个地方用到某个依赖时,由容器负责提供使用。

DI 容器有时候也叫 IoC 容器,叫什么不重要,属于个人理解的区别,只要清楚

总结

本文中的描述比较直白,缺乏一定的严谨性,主要是为了方便大家从作用的角度去理解 DI 和 IoC 。

那就再总结一次:

  • DI:应用需要的依赖,由框架负责管理,注入到需要它的地方
  • IoC:创建和维护依赖的工作由开发者转交给框架

如果有什么错误表述,概念上的误解,欢迎在评论区留下宝贵的意见,感谢阅读 🍔