Skip to content

依赖注入和控制反转

Nest 设计模式

控制反转就是把对象的控制权交给了容器,注入依赖就是帮助容器去管理这些对象之间的关系。

  • IoC控制反转 不是一种实现的方法或技巧,而是一种笼统的软件设计思想,它更多表述的是为什么要控制反转、控制反转的目的是什么。
  • DI依赖注入 则是控制反转的一种实现方法,怎么去实现控制反转依赖注入只是其中的一种方法。

IoCDI

如果我们需要实现一段代码逻辑,常规情况下是递进实现,跟搭积木一样的。

比如实际生活中以汽车来举例子:

image-20230806212007557

汽车抽象为一个类,其中包含引擎、品牌等等基础依赖类,当它们组合在一起就是一个完整的汽车,用代码来解释就是下面的例子:

定义两个依赖类: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()

image-20230806215510355

上面这种写法暴露了一些问题,比如当 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()

如有转载或 CV 的请标注本站原文地址