依赖倒置原则[DIP]中的"Abstractions should not depend on details. Details should depend on abstractions"是什么意思?

What is mean by "Abstractions should not depend on details. Details should depend on abstractions" in Dependency inversion principle[DIP] means?

在问这个问题之前我想说 this Whosebug 中的问题与我的问题非常相似,但仍然概念不明确非常混乱。

我想了解依赖倒置原理,但我无法完全理解它?

下面是DIP说的两点

A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend on details. Details should depend on abstractions.

我能掌握第一点,但我无法掌握第二点,看起来两者是一样的。在 Whosebug 和其他网站上进行大量搜索后,我能够理解两者都在尝试说不同的事情,但我无法理解。

让我们考虑一个例子:

让我们考虑一下 SalaryCalculator class [高级模块],它用于计算员工的工资。其中使用 BonusCalculator [高级模块] 来计算薪水,如下所示。由于 SalaryCalculator 使用 BonusCalculator,它违反了 “高级模块不应依赖于低级模块”的第一点。两者都应该依赖于抽象。

所以我们在两者之间引入了抽象,如下所示:

这里的细节[低级和高级模块]依赖于抽象和抽象不依赖于细节。那么在 DIP 中,第二点试图说明什么? 如果两者相同,为什么做成两点?

如果有人给我一个代码示例,那将非常有用。

这个问题真正触及了为什么 OOP 有用,以及为什么抽象对计算机科学如此重要的核心。基本上,当我们想向软件用户隐藏复杂性(细节)时,我们会使用抽象。

例如,如果我正在编写奖金计算器,而你正在编写薪水计算器,我希望能够在不破坏你的应用程序的情况下调整我的代码。

这需要我们都同意一个永远不会改变的特定抽象。我为您提供了访问我的代码功能的方法,我向您保证调用这些方法将始终为您提供相同的结果,即使我的实现的 "details" 可以随时间自由更改。

所以回到最初的问题:

一个。 高级模块不应该依赖于低级模块。两者都应该依赖于抽象。

  • 通过"abstracting away"低级模块(奖金计算器)中包含的功能,如果您发现我的服务很垃圾,您应该可以相对容易地自由切换到别人的奖金计算器。
  • 这是因为您通过抽象保护了自己免受 "details" 我的代码的影响。

乙。 抽象不应依赖于细节。细节应该取决于抽象。

  • 如果您的抽象依赖于我的代码的细节,那么您将不得不重写所有内容以切换到新的奖金计算器!那会破坏目的。

代码示例(javascript):

  • 假设我们有一个名为 "sum" 的抽象,它只计算两个数字的总和。你是这个函数的消费者,想像这样使用它:sum(2,2) = 4.
  • 现在假设有两个不同的模块(函数)计算总和。

    1. function sum(a, b) { return a + b }
    2. function sum(b, a) { return b + a }
  • 显然这些函数是完全相同的,但想象一下,如果这是一个复杂的计算,有许多不同的方法来完成结果,并且每种方法都有非常不同的运行时性能。您可以自由测试哪个函数更适合您,同时使用相同的接口:只需调用 sum()。抽象不依赖细节

  • 通过这样做,您的高级模块也不再依赖于低级模块,因为您可以相对轻松地自由尝试不同的低级模块。

抱歉,这个答案有点乱。希望这对您有所帮助!

让我们进一步分解 B 部分。

抽象不应依赖于细节。这可能意味着您的接口声明(您的抽象)应避免包含具体类型。想一想 distance(int X1, int Y1, int X2, int Y2)distance(Point A, Point B) 之间的区别。如果您在浮点、lat/lon 或极坐标系中测量坐标怎么办?如果换成3D的space呢?您必须重新实现每个使用距离函数的例程。

细节应该依赖于抽象。在可行的情况下,继续使用抽象层来避免对具体类型的依赖。

一切都是为了尽量减少变化的影响。您的代码对其他事物的依赖越少,其他代码的更改就越多。