美文网首页
怎样理解依赖反转

怎样理解依赖反转

作者: Mob_Developer | 来源:发表于2024-09-07 04:45 被阅读0次

依赖反转的定义

在面向对象开发中有一个基本的设计原则叫依赖反转/倒置(dependency inversion)。维基百科对于它的定义如下:

  1. 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
  2. 抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。

剖析

但是当我看到这样的定义时,我是十分困惑的。什么是依赖?什么是反转?为什么定义了抽象接口,作为中间层会被称为依赖反转?如果你和我有相同的疑问,不妨跟我一起看下去。

我们先看一下下面这段传统的示例代码片段 1:

class B {
  void mb(){}
}

class A {
  public void cb() {
      (new B()).mb();
  }
}

上面的代码非常简单,在 Acb 方法创建了 B 的对象并调用了它的 mb 方法。 在这里 A 需要 B 才能实现 cb 的功能,所以我们称 A 依赖于 B。 这里我们同时需要知道的是 A 是 “高层次”, “B” 属于低层次。

按照‘‘依赖反转’’的定义, 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口, 所以 “B” 是要抽象出来的。我们把上面的代码改造成下面的代码片段 2:

interface IB {
  void mb();
}

class B implements IB {
  void mb(){}
}

class A {
  public void cb(IB b) {
      b.mb();
  }
}

这样A 不再依赖于具体的 B 的对象,而是依赖于 IB 接口。B 也实现了 IB 接口。可以说这里我们已经就满足了 “依赖反转” 的定义:

  1. A 不依赖于 BAB 都依赖于抽象接口 IB
  2. IB 不依赖于具体实现 AB。而具体实现 BA 则应该依赖于抽象接口 IB

总结下,现在依赖关系的变化:A -> B 变成了 A -> IB, B -> IB

问题来了,“反转” 从何而来?不妨继续看看下面的代码。

void main() {
  (new A()).cb();
}

在这段代码中,我们在 main 函数,调用了 代码片段 1A.cb 方法。不难理解,在这里,main 函数其实又是 A 的高层次。我们实现的是高层次依次调用低层次的具体实现, main -> new A() -> new B()。接着我们看看 main 调用 代码片段2 会有什么不同?

void main() {
  IB b = new B();
  (new A(b)).cb();
}

写到这里,我忽然觉得不需要解释什么了,代码已经说明了一切。对于高层次的 A 来讲,低层次的 B 却被先创建了出来,变成了 main -> new B() -> new A()。这就是 “反转” 的意思。低层次的实现被推迟到了更高的层次。

总结,依赖反转是从调用者的视角看的。传统的代码实现,是高层次调用低层次的具体实现,但是使用了依赖反转后,低层次的具体实现,被提升到了更高的层次。

扩展

依赖反转的优点:

上面我们结合概念和代码,解释了什么是依赖反转。那么依赖反转的优点是什么?

  1. 解耦合:A 不再强绑定于 B, 任何实现了 IB 接口的类,都能被传入 A 中;
  2. 扩展性:任何实现了 IB 接口的类,都能被传入到 A 中,而无需修改 A 的代码;

其他 “依赖” 的表现形式

class A  {
   C mc;
   public A(C c) {
    mc = c; 
  }
  public setD(D d) {
    ....
  }
}

在上面的构造函数, setD 都属于依赖。想必会加深对依赖的理解。

依赖反转和依赖注入是什么关系

依赖注入是依赖反转的一种具体的实现方式。

自问自答

自问: 如果没有创建 IB 接口,通过下面的方式实现,是不是也是依赖反转?

class A {
  public void cb(B b) {
      b.mb();
  }
}

void main() {
    B b = new B();
    (new A(b)).cb();
}

自答: 首先毫无疑问这是依赖注入,依赖注入是依赖反转的一种具体实现,所以是。在 dart 语言中,类本身也是接口,从这种层面上来讲,完全是 ok 的。但是从 java 来讲又不完全是,因为不符合依赖反转的具体定义,只能传 B 的对象, 这就破坏了代码的灵活性、扩展性、非耦合性,就算它是,也是一种很烂的实现。

所以我们应该严格按照依赖反转的定义去编写我们具体的代码。

相关文章

网友评论

      本文标题:怎样理解依赖反转

      本文链接:https://www.haomeiwen.com/subject/jjvjljtx.html