【设计模式】依赖注入和控制反转

2023/11/30 23:40:372022/12/08 16:45:52

依赖注入 DI 和控制反转 IOC

依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不开谁。 ——知乎回答open in new window中的评论

本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象。 ——同回答下的另一个评论

依赖项注入(DI)是一种设计模式,在这种设计模式中,类会从外部源请求依赖项而不是创建它们。 ——Angular 中的依赖注入open in new window

什么是依赖注入/控制反转

假设有两个类 A 和 B,如果要在 A 中调用 B 的成员方法,就要先在 A 中 new 一个 B 的实例,如果 A 中的多个方法中都需要调用 B 的成员方法,就要调用多个 new

class A {
  text1() {
    const b = new B();
    b.show();
  }

  text2() {
    const b = new B();
    b.show();
  }
}

class B {
  show()
}

这样的实现方式会导致 A 和 B 紧密耦合在一起。

这时候通过一个第三方来执行 new B() 的操作,这叫做控制反转。

A 要依赖 B,必然要使用 B 的实例:

  • 通过 A 的接口,把 B 的实例传入。
  • 通过 A 的构造,把 B 的实例传入。
  • 通过设置 A 的属性,把 B 的治理传入。

将 B 的实例传入 A 的过程就叫依赖注入。

用传入实例的方式让 A 可以使用 B 的实例,这就是依赖注入,这让 A 和 B 两个类解耦。

由第三方控制实例的构造和销毁,这就是控制反转。

为什么要用依赖注入

通过控制反转和依赖注入,可以让相互依赖的类减少耦合。

可以减少重复的 new B() 代码。

怎么用依赖注入

依赖注入是一种设计模式,一些框架中实现了自己的 DI 框架,可以用他们提供的方式使用。

Nest 中的依赖注入open in new window

Angular 中的依赖注入open in new window

实现一个简单的依赖注入模式

未使用依赖注入模式时的依赖关系

ClassA 提供两个方法:submit()getInfo()

// a.js
export class ClassA {
  submit()
  getInfo()
}

如果其他模块要使用这两个方法,需要导入并实例化 ClassA

// b.js
import {ClassA} from './a.js'

const classA = new ClassA()

export class classB {
  constructor() {
    classA.getInfo()
  }
}

main 函数

// main.js
import {ClassB} from './b.js'

new ClassB()

这样的副作用就是 ClassB 完全依赖 ClassA,这就形成了耦合。

使用依赖注入后的依赖关系

改造 ClassB

// b.js
export class classB {
  constructor(classA) {
    classA.getInfo()
  }
}

改造 main

import {ClassA} from './a.js'
import {ClassB} from './b.js'

const classA = new ClassA()
const classB = new ClassB(classA)

像这样,在 main 中将实例 classA 传递给 ClassB,就实现了 ClassBClassA 的解耦。

参考

什么是依赖注入open in new window

如何用最简单的方式解释依赖注入?依赖注入是如何实现解耦的?open in new window

Angular 中的依赖注入open in new window

Node.js设计模式