15

DIP 指出:

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

OCP 规定:

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

我认为如果我们满足 DIP,它也会涵盖 OCP,那么,为什么我们将这两个原则分开?

4

5 回答 5

7

我认为遵守 DIP 更容易遵守 OCP。但是,一个不能保证另一个。

例如,我可以创建一个类,该类的方法采用base. 如果base是一个抽象类,那么我将遵守 DIP,因为我已经反转了对调用者的依赖。但是,如果该方法中的代码执行以下操作:

if (base is derived)
    (derived)base.DoSomethingSpecificToDerived;
elsif (base is evenMoreDerived)
    (evenMoreDerived)base.DoSomethingSpecificToEvenMoreDerived;

然后它不符合 OCP,因为我每次添加新衍生产品时都必须对其进行修改。

这是一个非常人为的例子,但你明白我的意思。

于 2013-08-25T11:11:13.677 回答
7

Bob Martin 叔叔将开闭原则 (OCP) 和依赖倒置原则 (DIP) 作为 SOLID 原则中的两个推广开来,他自己表示 DIP 源于 OCP 和 Liskov 替换原则的应用:

在本专栏中,我们将讨论 OCP 和 LSP 的结构含义。严格使用这些原则所产生的结构本身可以概括为一个原则。我称之为“依赖倒置原则”(DIP)。

Robert C. Martin,工程笔记本,C++ 报告,1996 年。

所以你说得对,每个 DIP 实例都是 OCP 的一个实例,但 OCP 更通用。这是我最近遇到的 OCP 但不是 DIP 的用例。许多 web 框架都有信号的概念,在一个动作中,一个信号被触发。发送信号的对象完全不知道注册了信号的听众。每次你想为信号添加更多的监听器时,你可以在不修改发送者的情况下这样做。

这显然是 OCP(“禁止修改,开放扩展”)的例证,但不是 DIP,因为发送者不依赖于任何东西,所以谈论它是否依赖于更抽象或更抽象的东西是没有意义的。

更一般地说,您可以说观察者模式(GoF 模式之一)描述了如何遵守 OCP 而不是 DIP。阅读 GoF 的书,看看哪些与 OCP 相关,以及其中有多少与 DIP 无关,这会很有趣。

于 2018-07-22T19:22:49.747 回答
5

DIP 告诉您如何组织依赖关系。它不会告诉您何时完成特定界面。

粗略地说,OCP 的信息是拥有完整但简约的界面。换句话说,它会告诉您何时完成界面,但不会告诉您如何实现这一点。

在某种意义上,DIP 和 OCP 是正交的。


那么,为什么我们要把这两个原则分开呢?

至于设计模式和命名原则,它们几乎都有一个共同点:

  1. 找出变化并封装(隐藏)它。

  2. 更喜欢聚合而不是继承。

  3. 设计到接口。

即使命名的模式和原则在某种意义上部分重叠,它们也会告诉你比上述三个一般原则更具体的东西(在更具体的情况下)。

于 2013-08-25T11:15:57.147 回答
1

@CS的好答案。总结一下,

  • DIP 是 OCP 的扩展,所以
  • 当我们满足 DIP 时,我们通常也满足 OCP。
  • 反过来则不然,我们可以设想符合 OCP 的 DIP 违规。这是另一个(Java)示例。
public abstract class MyClass {
    DependencyOne d1;
    DependencyTwo d2;

    MyClass() {
        d1 = new DependencyOne();
        d2 = new DependencyTwo();
    }
}

OCP 是满意的,因为我们可以扩展类。违反了 DIP,因为我们直接实例化了依赖项。

现在的挑战是,我们能否想到一个符合 DIP 的 OCP 违规行为。我能想到的最好的例子是注释。在Java中,我们使用@Deprecated注解来标记可以修改的代码,从而违反了OCP。同时,这段代码在抽象和依赖方面可能完全符合 DIP。某些库使用@Beta注释来达到类似的效果。

除了没有依赖关系的类的无效示例之外,我无法想象一个符合 DIP 且无法扩展的示例,这不是很有趣。我会说 DIP 意味着对扩展的开放性。但是,可能存在 DIP 并不意味着封闭性修改的边缘情况。

于 2021-09-23T00:25:13.467 回答
0

OCP 使依赖类易于使用。OCP 通过将旧实现与新版本分离来实现接口的异步使用。它允许依赖它的事物继续依赖它,即使面对其他目的的变化。这样一来,一个班级就不必关心是谁在给它打电话。

DIP 做了几件事。它使依赖外部类变得容易。依赖注入通过鼓励将创建职责与消费分离来实现依赖关系的替换。该模式没有创建要使用的外部依赖项,而是声明它应该由外部提供。最终,这鼓励了幂等的代码(不改变外部状态的代码)。幂等代码很好,因为可以验证它只执行立即可见的操作。它没有外部副作用。它非常可测试、易于理解和可读。

于 2013-08-27T01:11:22.680 回答