依赖注入和控制反转
Nest
设计模式
控制反转就是把对象的控制权交给了容器,注入依赖就是帮助容器去管理这些对象之间的关系。
IoC控制反转
不是一种实现的方法或技巧,而是一种笼统的软件设计思想,它更多表述的是为什么要控制反转、控制反转的目的是什么。DI依赖注入
则是控制反转的一种实现方法,怎么去实现控制反转,依赖注入只是其中的一种方法。
IoC
和 DI
如果我们需要实现一段代码逻辑,常规情况下是递进实现,跟搭积木一样的。
比如实际生活中以汽车来举例子:
汽车抽象为一个类,其中包含引擎、品牌等等基础依赖类,当它们组合在一起就是一个完整的汽车,用代码来解释就是下面的例子:
定义两个依赖类:Brand品牌
、Engine引擎
ts
// 依赖1:Brand 品牌
class Brand {
public name: string
public belongsToNationality: string
constructor(name: string, belongsToNationality: string) {
console.log('🚀 ~ 初始化品牌')
this.name = name
this.belongsToNationality = belongsToNationality
}
}
ts
// 依赖2:Engine 引擎
class Engine {
public brakeType: string
public cylinder: number
constructor(brakeType: string, cylinder: number) {
console.log('🚀 ~ 初始化引擎')
this.brakeType = brakeType
this.cylinder = cylinder
}
}
定义一个上层应用类 Car
,依赖刚才定义的两个底层类:
ts
class Car {
// 自属性
public color: string
public type: string
// 依赖属性
public brandName: string
public brandNBelongsToNationality: string
public engineBrakeType: string
public engineCylinder: number
// 依赖类
public brand: Brand
public engine: Engine
constructor(
color: string,
type: string,
brandName: string,
brandNBelongsToNationality: string,
engineBrakeType: string,
engineCylinder: number
) {
console.log('🚀 ~汽车类初始化')
this.color = color
this.type = type
this.brand = new Brand(brandName, brandNBelongsToNationality)
this.engine = new Engine(engineBrakeType, engineCylinder)
}
run() {
console.log('🚀 ~ 芜湖,跑起来了')
}
}
并将应用类实例化使用:
ts
const car = new Car('红色', '轿车', '宝马', '德国', '汽油发动机', 2)
car.run()
上面这种写法暴露了一些问题,比如当 Car
的依赖项,后续如果需要对它们进行扩展并进行传参,就需要在依赖类构造函数中进行传参,上层类也需要跟着改变进行传参。
将上面的例子,稍作修改,更改 Brand
的入参传参:
ts
// 依赖1:Brand 品牌
class Brand {
public name: string
public belongsToNationality: string
public logo: string
constructor(
name: string,
belongsToNationality: string,
logo: string
) {
console.log('🚀 ~ 初始化品牌')
this.name = name
this.logo = logo
this.belongsToNationality = belongsToNationality
}
}
ts
class Car {
// 自属性
public color: string
public type: string
// 依赖属性
public brandName: string
public brandNBelongsToNationality: string
public brandLogo: string
public engineBrakeType: string
public engineCylinder: number
// 依赖类
public brand: Brand
public engine: Engine
constructor(
color: string,
type: string,
brandName: string,
brandNBelongsToNationality: string,
brandLogo: string,
engineBrakeType: string,
engineCylinder: number
) {
console.log('🚀 ~汽车类初始化')
this.color = color
this.type = type
this.brand = new Brand(
brandName,
brandNBelongsToNationality,
brandLogo
)
this.engine = new Engine(engineBrakeType, engineCylinder)
}
run() {
console.log('🚀 ~ 芜湖,跑起来了')
}
}
ts
const car = new Car(
'红色',
'轿车',
'宝马',
'德国',
'https://p8.itc.cn/images01/20210425/61183c2a47a649e88bc52821d2c8a5ef.jpeg',
'汽油发动机',
2
)
car.run()
可以看到,依赖类如果只是拓展一个属性,就需要修改依赖类、上层类、应用类三个文件。对于顶层的应用类来说,其实并不关心依赖类需要什么参数,参数也不需要经过应用类传递,当前的代码更多耦合在了应用类,使得依赖类和应用类之间发生了强耦合。
这就是没有 IoC控制反转
代码所带来的的问题,给维护和拓展都带来了不小的麻烦。
接下来使用 IoC控制反转
的设计模式来改造一下应用类 Car
的代码:
ts
class Car {
// 自属性
public color: string
public type: string
constructor(color: string, type: string, public brand: Brand, public engine: Engine) {
console.log('🚀 ~汽车类初始化')
this.color = color
this.type = type
}
run() {
console.log('🚀 ~ 芜湖,跑起来了')
}
}
应用层代码:
ts
// 将依赖对象的控制权交由外部控制,不再与应用层之间发生强耦合,这就是 IoC控制反转
const brand = new Brand('宝马', '德国', 'https://p8.itc.cn/images01/20210425/61183c2a47a649e88bc52821d2c8a5ef.jpeg')
const engine = new Engine('汽油发动机', 4)
const car = new Car('红色', '轿车', brand, engine)
car.run()
将依赖对象的控制权交由外部控制,这个外部可以是IoC容器
,也可以是其他的全局单例对象,控制已经发生了反转,依赖类不再与应用层之间发生强耦合,这就是 IoC控制反转
。
实现最小自动挡 IoC
容器
ts
class Container {
private dependencies: { [key: string]: any } = {}
register(key: string, dependency: any) {
this.dependencies[key] = dependency
}
getDependency(key: string) {
if (this.dependencies[key]) {
return this.dependencies[key]
}
throw new Error(`依赖 ${key}未被注册到IoC容器中`)
}
}
ts
const container = new Container()
container.register(
'brand',
new Brand('宝马', '德国', 'https://p8.itc.cn/images01/20210425/61183c2a47a649e88bc52821d2c8a5ef.jpeg')
)
container.register('engine', new Engine('汽油发动机', 4))
const car = new Car('红色', '轿车', container.getDependency('brand'), container.getDependency('engine'))
car.run()