控制反转和依赖注入以及依赖反转

115 阅读3分钟

控制反转(Inversion Of Control, IOC)

我们有 SinglePageContainer 类和 SinglePage1 类。看如下代码👇。

class SinglePage1 {
  constructor() {}
  createdPage1() {
    // TODO
  }
  parsePage1() {
    // TODO
  }
  renderPage1() {
    // TODO
  }
}

class SinglePageContainer {
  public sp;
  constructor() {
    // 做一些工作
    this.sp = new SinglePage1();
  }

  start() {
    this.sp.createdPage1();
    this.sp.parsePage1();
    this.sp.renderPage1();
  }
}

类图如下👇

此时 SinglePageContainer 类与 SinglePage1 类强耦合。 随着业务越来越复杂,单页面越来越多,我们发现再拆分 SinglePageContainer 和 SinglePage1 变得非常困难。我们如果想要享受到 SinglePageContainer 所做的工作,我们必须完全拷贝一份 SinglePageContainer 的代码,将 SinglePage1 修改为 SinglePageN。 所以需要设计出 SinglePageContainer 不直接依赖于 SinglePage1 的程序。我们将 SinglePageN 抽象为一个接口(基于接口而非实现)来进行 SinglePageContainer 类的重构。看如下代码👇

code.juejin.cn/pen/7145660…

重构后,我们有 SinglePage1、SinglePage2、两个单页类,它们基于 ISinglePage 接口定义,有着通用的成员变量和成员方法,SinglePageContainer 与 SinglePage1 解偶。

这就是控制反转—— Container 类作为一个骨架,组装 SinglePage 对象、管理 SinglePage 对象的行为,却不关心 SinglePage 的业务具体实现。后续开发者只需要关心 Container 类预留出来的扩展点(sp),基于 ISinglePage 来扩展新的单页,再编写不同用户的业务行为,就可以驱动页面的整个程序运转。

Q:什么是控制?

A:

  • 谁控制谁的什么?
    • 控制程序的运转;
    • 在我们运用控制反转之前,SinglePageContainer 和 SinglePage1 共同控制整个程序的运转(框架逻辑/业务逻辑);
    • 在我们运用控制反转之后,Container 基于 ISinglePage 控制整个程序(框架逻辑)运转;
      • (此时分为两个部分 框架逻辑/业务逻辑)
    • 所以,这里的控制是指,Container 控制着整个程序(框架逻辑)运转;

Q:什么是反转?

A:

  • 谁反转谁的什么?
    • 反转的是程序(框架逻辑)的控制权;
    • 在我们运用控制反转之前,SinglePageContainer 和 SinglePage1 共同控制整个程序(框架逻辑/业务逻辑)的运转;
    • 在我们运用控制反转之后,Container 利用 ISinglePage 接口,将 SinglePage1 的控制(框架逻辑)程序流程的权利收回到 Container 中;

类图如下👇

Cotainer 成员变量引用了 ISinglePage 接口,SinglePageN 作为 ISinglePage 接口的实现类,这套关系让 Container 可以不用关心 SinglePageN 的具体实现,就可以让整个程序跑起来。

tips: 控制反转并非具体的编码实现,而是一种指导思想

依赖注入(Dependency Injection, DI)

tips: 依赖注入是一种具体的编码技巧

上面控制反转代码中对依赖注入有了应用:外部实例化好对象(依赖)后,通过构造函数或函数参数等方式传入(注入)到类中

const sp1 = new Container(new SinglePage1());
const sp2 = new Container(new SinglePage2());

依赖反转原则(Dependency Inversion Principle,DIP)

tips: 有时候也叫依赖倒置原则

原文:

High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.

译:

高层模块不应该依赖底层模块,高层模块和底层模块应该通过抽象来互相依赖。此外,抽象不要依赖具体实现,具体实现依赖抽象。

这里高层模块为 Container 底层模块为 SinglePageN 。我们在 SinglePageN 中基于抽象 ISinglePage 的编写具体业务代码,在 Container 中依赖抽象出来的 ISinglePage 来实现 Container 和 SinglePageN 的依赖关系。