控制反转和依赖注入

133 阅读3分钟

控制反转和依赖注入

  1. 控制反转(IoC): 在传统的编程中,应用程序通常会控制自己的流程,并且自己负责创建和管理对象。而控制反转就是将这种控制的权力反转,让外部框架或容器来控制应用程序中各种组件的创建和管理。简而言之,不再由你的代码掌握一切,而是由外部的框架或容器掌握。

  2. 依赖注入(DI): 依赖注入是一种实现 IoC 的方式,它通过将一个对象需要的依赖关系从外部注入(传递)到该对象中。这样,对象不再负责自己的依赖,而是由外部负责将依赖注入给对象。

简而言之,控制反转是一种思想,它让应用程序的控制权力反转到外部;而依赖注入是实现控制反转的一种具体方式,它通过注入对象的依赖关系来减轻对象对其他对象的控制。这两个概念通常一起使用,以实现更灵活、可扩展、可测试的代码结构。

代码示例

先来看一个不使用控制反转的例子。

不使用控制反转和依赖注入

首先我们有两个基础类,Engine类和Skylight类,它们分别代表发动机和天窗。

class Engine {
  constructor(public cylinder: number) {
    console.log(`这是${cylinder}缸的发动机`);
  }

  start() {
    console.log(`${this.cylinder}缸发动机启动`);
  }

  stop() {
    console.log(`${this.cylinder}缸发动机启动`);
  }
}

class Skylight {
  constructor(public hasSkylight: boolean) {
    console.log(hasSkylight ? '有天窗' : '无天窗');
  }

  open() {
    if (this.hasSkylight) {
      console.log(`打开天窗`);
    }
  }

  shut() {
    if (this.hasSkylight) {
      console.log(`关闭天窗`);
    }
  }
}

然后我们有个基于这两个基础类的Car类,它需要Engine和Skylight两个实例。

class Car {
  public Engine: Engine;
  public Skylight: Skylight;

  constructor(
    cylinder: number,
    hasSkylight: boolean,
  ) {
    this.Engine = new Engine(cylinder);
    this.Skylight = new Skylight(hasSkylight);
  }
  run() {
    this.engine.start();
    this.skylight.open();
    console.log('汽车开始行驶');
  }
  stop() {
    this.engine.stop();
    this.skylight.shut();
    console.log('汽车停止行驶');
  }
}

最后我们创建一个Car实例,并调用run方法,可以看到控制台输出。

const car = new Car(4, true);
car.run();

控制台输出:

4缸的发动机
有天窗
汽车开始行驶

可以看到,这个例子中,Car类负责控制Engine和Skylight两个实例的创建,并且需要知道它们的构造函数需要什么参数。

假如以后Engine或Skylight这两个基础类的入参需要增多时,Car类需要跟着修改,这显然不是我们想要的,耦合太重了。

下面看一下如何使用控制反转和依赖注入来解决这个问题。

使用控制反转和依赖注入

还是使用上面那两个基础类,修改一下Car类的代码

class Car {
  constructor(
    public Engine: Engine,
    public Skylight: Skylight,
  ) {}

  run() {
    this.engine.start();
    this.skylight.open();
    console.log('汽车开始行驶');
  }
  stop() {
    this.engine.stop();
    this.skylight.shut();
    console.log('汽车停止行驶');
  }
}

可以看到,Car类不再负责Engine和Skylight的创建,而是接受这两个类的实例。

当外部想创建Car类时,需要先将这两个依赖注入到Car类中。

这就是所谓的依赖注入。

const fourEngine = new Engine(4);
const hasSkyLight = new Skylight(true);

const car = new Car(fourEngine, hasSkyLight);
car.run();

这么做的好处是,当后续Engine类或Skylight类的发生变化时,不再需要修改Car类,只需要修改这两个类和创建的地方即可。

也就是说,Car类将控制创建Engine类或Skylight类的权力交了出去,由外部容器创建,这就是所谓的控制反转。

这个外部容器,也称之为控制反转容器(IoC容器)。

参考