在 NestJS 中,IOC(控制反转)和 DI(依赖注入)是核心概念,它们使得应用程序的模块化和解耦变得更加容易。
控制反转(IOC,Inversion of Control)
控制反转是一个设计原则,它的基本思想是将控制逻辑从应用程序代码中抽离出来,让框架或容器来负责控制逻辑的管理。在传统的编程模型中,应用程序代码负责创建和管理依赖关系。而在控制反转的模型中,框架负责这些操作,从而将控制的“责任”反转给了框架。
依赖注入(DI,Dependency Injection)
依赖注入是实现控制反转的一种常见方式。在 NestJS 中,依赖注入指的是将一个对象的依赖关系(即该对象所依赖的其他对象)注入到对象中,而不是由对象自己创建这些依赖。
在 NestJS 中,DI 的工作流程如下:
-
定义服务:首先,你定义一个服务(比如 UserService),这个服务可能依赖于其他服务(比如 LoggerService)。
-
提供者:在 NestJS 中,服务通常是作为提供者(provider)进行注册的。提供者可以是服务、工厂函数、值等。
-
注入依赖:使用 @Injectable() 装饰器标记的类可以被 NestJS 的依赖注入系统管理。在构造函数中,使用 constructor(private readonly logger: LoggerService) 语法来注入依赖。NestJS 在运行时会自动实例化这些依赖,并将它们注入到服务中。
-
模块化:所有的提供者和服务在 NestJS 中都是模块的一部分,模块会定义哪些提供者是可用的,并且可以指定哪些模块和提供者是外部可用的。
例子
未使用控制反转和依赖注入
// 外套 类
class Overcoat {
name: string
constructor(name: string) {
this.name = name
}
}
class People {
override: Overcoat
constructor() {
this.override = new Overcoat('巴黎世家')
}
showMyOverride() {
console.log(this.override.name)
}
}
const person = new People()
person.showMyOverride() // 输出 "巴黎世家"
潜在坏处:
-
违反了单一职责原则:People 类负责了它的核心职责(比如表示一个人)之外的其他职责,例如创建 Overcoat 的实例。这会导致类的职责不清晰,可能会导致代码难以维护和扩展。
-
系统的低内聚性和高耦合:硬编码的依赖关系导致 People 类和 Overcoat 类之间的耦合性较高。这意味着如果 Overcoat 类的实现需要改变,或者Overcoat 类需要不同的实现,那么 People 类也需要相应地做出修改。
-
测试难度增加:直接创建依赖的实例使得单元测试变得复杂。当测试 People 类时,需要保证 Overcoat 类的实现按照预期工作。如果想用不同的 Overcoat 实例测试 People 类,就需要修改 People 类的代码,这违背了测试驱动开发的宗旨。
使用控制反转和依赖注入
// 外套 类
class Overcoat {
name: string
constructor(name: string) {
this.name = name
}
}
// 人物 类
class People {
override: Overcoat
constructor(override: Overcoat) {
this.override = override
}
showMyOverride() {
console.log(this.override.name)
}
}
// 中间件用于解耦
class Container {
modules: any
constructor() {
this.modules = {}
}
provide(key: string, module: any) {
this.modules[key] = module
}
get(key: string) {
return this.modules[key]
}
}
// 设置容器和依赖(组装过程)
const container = new Container()
container.provide('巴黎世家', new Overcoat('巴黎世家'))
container.provide('海澜之家', new Overcoat('海澜之家'))
// 使用依赖注入创建 People 实例
const overcoat = container.get('巴黎世家') as Overcoat
const people = new People(overcoat)
// 展示结果
people.showMyOverride()