Nest 可以看作是 Node.js 版本的 Spring 框架,解决了项目开发中一个重要的问题 -- 如何管理好项目的模块依赖。 下面来通过一个场景来理解依赖问题与解决思路。
TypeScript// 人 class People { work() { console.log("我是 People,我在工作 ... 螺丝出来了") } } // 螺丝生产车间 class ScrewWorkshop { private worker: People = new People() produce() { this.worker.work() } } // 工厂 class Factory { start() { const screwWorkshop = new ScrewWorkshop() screwWorkshop.produce() } } const factory = new Factory() // 工厂开工啦!!! factory.start()
CODE我是 People,我在工作 ... 螺丝出来了
科技进步,人干的活「拧螺丝」机器能干的更好了
TypeScript// 人 class People { work() { console.log("我是 People,我在工作 ... 螺丝出来了") } } // 机器 class Machine { work() { console.log("我是 Machine,我在工作 ... 螺丝出来了") } } // 螺丝生产车间 class ScrewWorkshop { // private worker: People = new People() private machine: Machine = new Machine() produce() { this.machine.work() } } // 工厂 class Factory { start() { const screwWorkshop = new ScrewWorkshop() screwWorkshop.produce() } } const factory = new Factory() // 工厂开工啦!!! factory.start()
CODE我是 Machine,我在工作 ... 螺丝出来了
后续科技还在进步, Machine 还在不断更换 我们的 Demo 代码很少,但业务的场景这样的硬编码替换可能会有很高的工作量 这是因为我们的模块之间较为耦合,需要的降低耦合
TypeScriptinterface Workable { work(): void } // 人 class People implements Workable { work() { console.log("我是 People,我在工作 ... 螺丝出来了") } } // 机器 class Machine implements Workable { work() { console.log("我是 Machine,我在工作 ... 螺丝出来了") } } // 螺丝生产车间 class ScrewWorkshop { private worker: Workable constructor(private _worker: Workable) { this.worker = _worker } produce() { this.worker.work() } } // 工厂 class Factory { start() { const worker = new People() const screwWorkshop = new ScrewWorkshop(worker) screwWorkshop.produce() const worker2 = new Machine() const screwWorkshop2 = new ScrewWorkshop(worker2) screwWorkshop2.produce() } } const factory = new Factory() // 工厂开工啦!!! factory.start()
我们主要做了两件事情
我们用张图来感受下两者的差异
改造前
改造后
再回看我们做的两件事情
定义了 Workable 接口,让ScrewWorkshop、People 和 Machine 都依赖接口,不相互依赖
设计原则
依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
不在 ScrewWorkshop 中直接创建 work 的实例,而是通过外界传入
控制反转
外界「factoy」 拿到了原有 ScrewWorkshop 对 People 和 Machine 的控制权「实例化」
依赖注入
就是只通过构造传入 ScrewWorkshop worker
依赖注入是实现控制反转的其中一种方式
在 Nest Custom providers 有这样一段话
Dependency injection is an inversion of control (IoC) technique wherein you delegate instantiation of dependencies to the IoC container (in our case, the NestJS runtime system), instead of doing it in your own code imperatively. Let's examine what's happening in this example from the Providers chapter.
在我们的 Demo 里 Factory 起到了类似 IoC container 的作用
Spring 和 Nest 的主要作用都是提供一个 IoC 的 container 来统一管理实例,降低模块耦合
假设 IoC 中已经帮我们管理了一个 type 为 Machine 的实例,我们现在有个新的类叫做
TypeScript@Injectable() class Machine { work() { console.log("我是 Machine,我在工作 ... 螺丝出来了") } } class Store { constructor(private machine: Machine) {} sell() { this.machine.work() } }
在使用 Store 的时候,machine 会直接传入进来,这一层是在框架层来处理的,也就是 NestJS 和 Spring 这一层,无需我们开发者去做实例化。 那这是怎么匹配过去的呢? 有很多种方式,在这个例子中,就是类型 Machine,这也是用的比较多的一种。
但我们知道 类型源于 TS,而真正运行的时候被转换成 JS 是不带有类型的,那框架层「factory」又是如何做类型匹配的呢? 在下一节,Nest 系列 - 2: 装饰器和类型解析 中会有介绍
Nest.js入门 —— 控制反转与依赖注入(一) 依赖倒置原则DIP 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)