2

在问这个问题之前,我想说stackoverflow中的这个问题与我的问题非常相似,但这个概念仍然不清楚很混乱。

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

以下是 DIP 所说的两点

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

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

让我们考虑一个例子:

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

在此处输入图像描述

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

在此处输入图像描述

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

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

4

2 回答 2

4

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

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

细节应该取决于抽象。 就实际情况而言,继续使用抽象层来避免对具体类型的依赖。

这一切都是为了尽量减少变化的影响。你的代码对其他东西的依赖越少,它就越能改变其他代码。

于 2018-10-19T00:42:22.383 回答
4

这个问题真正触及了为什么 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()。抽象不依赖于细节。

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

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

于 2018-10-17T14:51:52.670 回答