依赖倒置原则[DIP]中的“抽象不应依赖于细节。细节应依赖于抽象”是什么意思?

vic*_*cky 3 oop inversion-of-control design-principles solid-principles dependency-inversion

在问这个问题之前我想说的问题与我的问题非常相似,但概念仍然不清楚,非常令人困惑。

\n\n

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

\n\n

下面是DIP说的两点

\n\n
\n

A. 高层模块不应该依赖于低层模块。两者都应该依赖于抽象。B. 抽象不应依赖于细节。细节应该取决于抽象。

\n
\n\n

我能够理解第一点,但无法理解第二点,看起来两者是相同的。在 stackoverflow 和其他网站进行大量搜索后,我可以理解两者都试图说不同的事情,但我无法理解。

\n\n

让\xe2\x80\x99s 考虑一个例子:

\n\n

让\xe2\x80\x99s考虑SalaryCalculator类[高级模块],它用于计算员工的工资。其中使用BonusCalculator [高级模块]来计算工资,如下所示。由于 SalaryCalculator 使用 BonusCalculator,它\xe2\x80\x99s 违反了\xe2\x80\x9c 的第一点,高级模块不应依赖于低级模块。两者都应该依赖于抽象\xe2\x80\x9d。

\n\n

在此输入图像描述

\n\n

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

\n\n

在此输入图像描述

\n\n

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

\n\n

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

\n

小智 5

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

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

这要求我们双方就一个永远不会改变的特定抽象达成一致。我为您提供了访问我的代码功能的方法,并且我向您保证,调用这些方法将始终为您提供相同的结果,即使我的实现的“细节”可以随时间自由更改。

那么回到最初的问题:

A.高层模块不应该依赖于低层模块。两者都应该依赖于抽象。

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

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

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

代码示例(javascript):

  • 假设我们有一个名为“sum”的抽象,它只是计算两个数字的总和。您是该函数的使用者,并且希望像这样使用它:sum(2,2) = 4。
  • 现在假设有两个不同的模块(函数)来计算总和。

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

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

抱歉这个答案有点混乱。希望这可以帮助!


Joh*_*ers 5

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

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

细节应该取决于抽象。 只要可行,继续使用抽象层以避免对具体类型的依赖。

这一切都是为了尽量减少变化的影响。您的代码对其他事物的依赖越少,其他代码就越容易发生变化。